mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 10:40:01 +01:00
Merge branch 'master' into TemporaryHackishBranch
This commit is contained in:
commit
2ebdbc4ed9
31 files changed
+625
-284
No files matched your search
@@ -1265,6 +1265,8 @@ set(GPU_SOURCES
|
||||
GPU/Common/ShaderUniforms.h
|
||||
GPU/Common/ShaderCommon.cpp
|
||||
GPU/Common/ShaderCommon.h
|
||||
GPU/Common/ShaderTranslation.cpp
|
||||
GPU/Common/ShaderTranslation.h
|
||||
GPU/Common/SplineCommon.cpp
|
||||
GPU/Common/SplineCommon.h
|
||||
GPU/Common/StencilCommon.cpp
|
||||
|
||||
@@ -272,7 +272,7 @@ private:
|
||||
VkPhysicalDeviceMemoryProperties memory_properties{};
|
||||
|
||||
// Custom collection of things that are good to know
|
||||
VulkanPhysicalDeviceInfo deviceInfo_;
|
||||
VulkanPhysicalDeviceInfo deviceInfo_{};
|
||||
|
||||
// Swap chain
|
||||
int width_ = 0;
|
||||
@@ -299,8 +299,8 @@ private:
|
||||
|
||||
uint32_t queue_count = 0;
|
||||
|
||||
VkPhysicalDeviceFeatures featuresAvailable_;
|
||||
VkPhysicalDeviceFeatures featuresEnabled_;
|
||||
VkPhysicalDeviceFeatures featuresAvailable_{};
|
||||
VkPhysicalDeviceFeatures featuresEnabled_{};
|
||||
|
||||
std::vector<VkCommandBuffer> cmdQueue_;
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ bool VulkanPushBuffer::AddBuffer() {
|
||||
VkBufferCreateInfo b = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
b.size = size_;
|
||||
b.flags = 0;
|
||||
b.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
b.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
||||
b.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
b.queueFamilyIndexCount = 0;
|
||||
b.pQueueFamilyIndices = nullptr;
|
||||
|
||||
@@ -103,12 +103,20 @@ public:
|
||||
}
|
||||
|
||||
// "Zero-copy" variant - you can write the data directly as you compute it.
|
||||
// Recommended.
|
||||
void *Push(size_t size, uint32_t *bindOffset, VkBuffer *vkbuf) {
|
||||
assert(writePtr_);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
*bindOffset = (uint32_t)off;
|
||||
return writePtr_ + off;
|
||||
}
|
||||
void *PushAligned(size_t size, uint32_t *bindOffset, VkBuffer *vkbuf, int align) {
|
||||
assert(writePtr_);
|
||||
offset_ = (offset_ + align - 1) & ~(align - 1);
|
||||
size_t off = Allocate(size, vkbuf);
|
||||
*bindOffset = (uint32_t)off;
|
||||
return writePtr_ + off;
|
||||
}
|
||||
|
||||
size_t GetTotalSize() const;
|
||||
|
||||
|
||||
@@ -162,8 +162,12 @@ protected:
|
||||
public:
|
||||
virtual ~TessellationDataTransfer() {}
|
||||
// Send spline/bezier's control points to vertex shader through floating point texture.
|
||||
virtual void PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) {
|
||||
posStride = 4;
|
||||
texStride = 4;
|
||||
colStride = 4;
|
||||
}
|
||||
virtual void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) = 0;
|
||||
virtual void PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) {};
|
||||
};
|
||||
TessellationDataTransfer *tessDataTransfer;
|
||||
};
|
||||
@@ -69,7 +69,7 @@ class VulkanFBO;
|
||||
struct PostShaderUniforms {
|
||||
float texelDelta[2]; float pixelDelta[2];
|
||||
float time[4];
|
||||
bool video;
|
||||
float video;
|
||||
};
|
||||
|
||||
struct VirtualFramebuffer {
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
#include "ppsspp_config.h"
|
||||
|
||||
#if !defined(ANDROID)
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
@@ -34,6 +32,8 @@
|
||||
#include "ShaderTranslation.h"
|
||||
#include "ext/glslang/SPIRV/GlslangToSpv.h"
|
||||
#include "thin3d/thin3d.h"
|
||||
|
||||
#if !defined(ANDROID)
|
||||
#include "ext/SPIRV-Cross/spirv.hpp"
|
||||
#include "ext/SPIRV-Cross/spirv_common.hpp"
|
||||
#include "ext/SPIRV-Cross/spirv_cross.hpp"
|
||||
@@ -41,6 +41,7 @@
|
||||
#ifdef _WIN32
|
||||
#include "ext/SPIRV-Cross/spirv_hlsl.hpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern void init_resources(TBuiltInResource &Resources);
|
||||
|
||||
@@ -58,12 +59,12 @@ static EShLanguage GetLanguage(const Draw::ShaderStage stage) {
|
||||
|
||||
void ShaderTranslationInit() {
|
||||
// TODO: We have TLS issues on UWP
|
||||
#if !PPSSPP_PLATFORM(UWP)
|
||||
#if !PPSSPP_PLATFORM(UWP) && !defined(ANDROID)
|
||||
glslang::InitializeProcess();
|
||||
#endif
|
||||
}
|
||||
void ShaderTranslationShutdown() {
|
||||
#if !PPSSPP_PLATFORM(UWP)
|
||||
#if !PPSSPP_PLATFORM(UWP) && !defined(ANDROID)
|
||||
glslang::FinalizeProcess();
|
||||
#endif
|
||||
}
|
||||
@@ -83,7 +84,22 @@ cbuffer data : register(b0) {
|
||||
float2 u_texelDelta;
|
||||
float2 u_pixelDelta;
|
||||
float4 u_time;
|
||||
bool u_video;
|
||||
float u_video;
|
||||
};
|
||||
)";
|
||||
|
||||
static const char *vulkanPrologue =
|
||||
R"(#version 430
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
#extension GL_ARB_shading_language_420pack : enable
|
||||
)";
|
||||
|
||||
static const char *pushconstantBufferDecl = R"(
|
||||
layout(push_constant) uniform data {
|
||||
vec2 u_texelDelta;
|
||||
vec2 u_pixelDelta;
|
||||
vec4 u_time;
|
||||
float u_video;
|
||||
};
|
||||
)";
|
||||
|
||||
@@ -112,10 +128,78 @@ std::string Postprocess(std::string code, ShaderLanguage lang, Draw::ShaderStage
|
||||
return output;
|
||||
}
|
||||
|
||||
bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetadata, std::string src, Draw::ShaderStage stage, std::string *errorMessage) {
|
||||
std::stringstream out;
|
||||
|
||||
static struct {
|
||||
Draw::ShaderStage stage;
|
||||
const char *needle;
|
||||
const char *replacement;
|
||||
} replacements[] = {
|
||||
{ Draw::ShaderStage::VERTEX, "attribute vec4 a_position;", "layout(location = 0) in vec4 a_position;" },
|
||||
{ Draw::ShaderStage::VERTEX, "attribute vec2 a_texcoord0;", "layout(location = 1) in vec2 a_texcoord0;"},
|
||||
{ Draw::ShaderStage::VERTEX, "varying vec2 v_position;", "layout(location = 0) out vec2 v_position;" },
|
||||
{ Draw::ShaderStage::FRAGMENT, "varying vec2 v_position;", "layout(location = 0) in vec2 v_position;" },
|
||||
{ Draw::ShaderStage::FRAGMENT, "texture2D(", "texture(" },
|
||||
{ Draw::ShaderStage::FRAGMENT, "gl_FragColor", "fragColor0" },
|
||||
};
|
||||
|
||||
out << vulkanPrologue;
|
||||
if (stage == Draw::ShaderStage::FRAGMENT) {
|
||||
out << "layout (location = 0) out vec4 fragColor0;\n";
|
||||
}
|
||||
// Output the uniform buffer.
|
||||
out << pushconstantBufferDecl;
|
||||
|
||||
// Alright, now let's go through it line by line and zap the single uniforms
|
||||
// and perform replacements.
|
||||
std::string line;
|
||||
std::stringstream instream(src);
|
||||
while (std::getline(instream, line)) {
|
||||
char buffer[256];
|
||||
int vecSize, num;
|
||||
if (line.find("uniform bool") != std::string::npos) {
|
||||
continue;
|
||||
} else if (line.find("uniform sampler2D") == 0) {
|
||||
line = "layout(set = 0, binding = 0) " + line;
|
||||
} else if (line.find("uniform ") != std::string::npos) {
|
||||
continue;
|
||||
} else if (2 == sscanf(line.c_str(), "varying vec%d v_texcoord%d;", &vecSize, &num)) {
|
||||
if (stage == Draw::ShaderStage::FRAGMENT) {
|
||||
line = StringFromFormat("layout(location = %d) in vec%d v_texcoord%d;", num, vecSize, num);
|
||||
} else {
|
||||
line = StringFromFormat("layout(location = %d) out vec%d v_texcoord%d;", num, vecSize, num);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < ARRAY_SIZE(replacements); i++) {
|
||||
if (replacements[i].stage == stage)
|
||||
line = ReplaceAll(line, replacements[i].needle, replacements[i].replacement);
|
||||
}
|
||||
out << line << "\n";
|
||||
}
|
||||
|
||||
// DUMPLOG(src.c_str());
|
||||
// ILOG("---->");
|
||||
// DUMPLOG(LineNumberString(out.str()).c_str());
|
||||
|
||||
*dest = out.str();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, Draw::ShaderStage stage, std::string *errorMessage) {
|
||||
if (srcLang != GLSL_300 && srcLang != GLSL_140)
|
||||
return false;
|
||||
|
||||
if ((srcLang == GLSL_140 || srcLang == GLSL_300) && destLang == GLSL_VULKAN) {
|
||||
// Let's just mess about at the string level, no need to recompile.
|
||||
bool result = ConvertToVulkanGLSL(dest, destMetadata, src, stage, errorMessage);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(ANDROID)
|
||||
return false;
|
||||
#else
|
||||
|
||||
#if PPSSPP_PLATFORM(UWP)
|
||||
return false;
|
||||
#endif
|
||||
@@ -126,8 +210,8 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
|
||||
TBuiltInResource Resources;
|
||||
init_resources(Resources);
|
||||
|
||||
// Enable SPIR-V and Vulkan rules when parsing GLSL
|
||||
EShMessages messages = EShMessages::EShMsgDefault; // (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
|
||||
// Don't enable SPIR-V and Vulkan rules when parsing GLSL. Our postshaders are written in oldschool GLES 2.0.
|
||||
EShMessages messages = EShMessages::EShMsgDefault;
|
||||
|
||||
EShLanguage shaderStage = GetLanguage(stage);
|
||||
glslang::TShader shader(shaderStage);
|
||||
@@ -166,8 +250,6 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
|
||||
// Alright, step 1 done. Now let's take this SPIR-V shader and output in our desired format.
|
||||
|
||||
switch (destLang) {
|
||||
case GLSL_VULKAN:
|
||||
return false; // TODO
|
||||
#ifdef _WIN32
|
||||
case HLSL_DX9:
|
||||
{
|
||||
@@ -227,6 +309,5 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -21,8 +21,6 @@
|
||||
#include "GPU/Common/ShaderCommon.h"
|
||||
#include "thin3d/thin3d.h"
|
||||
|
||||
#ifndef ANDROID
|
||||
|
||||
struct TranslatedShaderMetadata {
|
||||
|
||||
};
|
||||
@@ -31,5 +29,3 @@ void ShaderTranslationInit();
|
||||
void ShaderTranslationShutdown();
|
||||
|
||||
bool TranslateShader(std::string *dst, ShaderLanguage destLang, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, Draw::ShaderStage stage, std::string *errorMessage);
|
||||
|
||||
#endif
|
||||
+30
-13
@@ -908,13 +908,22 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi
|
||||
const bool hasColor = (origVertType & GE_VTYPE_COL_MASK) != 0;
|
||||
const bool hasTexCoords = (origVertType & GE_VTYPE_TC_MASK) != 0;
|
||||
|
||||
tessDataTransfer->PrepareBuffers(pos, tex, col, count_u * count_v, hasColor, hasTexCoords);
|
||||
int posStride, texStride, colStride;
|
||||
tessDataTransfer->PrepareBuffers(pos, tex, col, posStride, texStride, colStride, count_u * count_v, hasColor, hasTexCoords);
|
||||
float *p = pos;
|
||||
float *t = tex;
|
||||
float *c = col;
|
||||
for (int idx = 0; idx < count_u * count_v; idx++) {
|
||||
memcpy(pos + idx * 4, points[idx]->pos.AsArray(), 3 * sizeof(float));
|
||||
if (hasTexCoords)
|
||||
memcpy(tex + idx * 4, points[idx]->uv, 2 * sizeof(float));
|
||||
if (hasColor)
|
||||
memcpy(col + idx * 4, Vec4f::FromRGBA(points[idx]->color_32).AsArray(), 4 * sizeof(float));
|
||||
memcpy(p, points[idx]->pos.AsArray(), 3 * sizeof(float));
|
||||
p += posStride;
|
||||
if (hasTexCoords) {
|
||||
memcpy(t, points[idx]->uv, 2 * sizeof(float));
|
||||
t += texStride;
|
||||
}
|
||||
if (hasColor) {
|
||||
memcpy(c, Vec4f::FromRGBA(points[idx]->color_32).AsArray(), 4 * sizeof(float));
|
||||
c += colStride;
|
||||
}
|
||||
}
|
||||
if (!hasColor)
|
||||
memcpy(col, Vec4f::FromRGBA(points[0]->color_32).AsArray(), 4 * sizeof(float));
|
||||
@@ -985,7 +994,6 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi
|
||||
ERROR_LOG(G3D, "Something went really wrong, vertex size: %i vs %i", vertexSize, (int)sizeof(SimpleVertex));
|
||||
}
|
||||
|
||||
|
||||
float *pos = (float*)(decoded + 65536 * 18); // Size 4 float
|
||||
float *tex = pos + count_u * count_v * 4; // Size 4 float
|
||||
float *col = tex + count_u * count_v * 4; // Size 4 float
|
||||
@@ -997,14 +1005,23 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi
|
||||
int num_patches_v = (count_v - 1) / 3;
|
||||
BezierPatch *patches = nullptr;
|
||||
if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) {
|
||||
tessDataTransfer->PrepareBuffers(pos, tex, col, count_u * count_v, hasColor, hasTexCoords);
|
||||
int posStride, texStride, colStride;
|
||||
tessDataTransfer->PrepareBuffers(pos, tex, col, posStride, texStride, colStride, count_u * count_v, hasColor, hasTexCoords);
|
||||
float *p = pos;
|
||||
float *t = tex;
|
||||
float *c = col;
|
||||
for (int idx = 0; idx < count_u * count_v; idx++) {
|
||||
SimpleVertex *point = simplified_control_points + (indices ? idxConv.convert(idx) : idx);
|
||||
memcpy(pos + idx * 4, point->pos.AsArray(), 3 * sizeof(float));
|
||||
if (hasTexCoords)
|
||||
memcpy(tex + idx * 4, point->uv, 2 * sizeof(float));
|
||||
if (hasColor)
|
||||
memcpy(col + idx * 4, Vec4f::FromRGBA(point->color_32).AsArray(), 4 * sizeof(float));
|
||||
memcpy(p, point->pos.AsArray(), 3 * sizeof(float));
|
||||
p += posStride;
|
||||
if (hasTexCoords) {
|
||||
memcpy(t, point->uv, 2 * sizeof(float));
|
||||
t += texStride;
|
||||
}
|
||||
if (hasColor) {
|
||||
memcpy(c, Vec4f::FromRGBA(point->color_32).AsArray(), 4 * sizeof(float));
|
||||
c += colStride;
|
||||
}
|
||||
}
|
||||
if (!hasColor) {
|
||||
SimpleVertex *point = simplified_control_points + (indices ? idxConv.convert(0) : 0);
|
||||
|
||||
@@ -226,7 +226,6 @@ void FramebufferManagerGLES::Init() {
|
||||
// Workaround for upscaling shaders where we force x1 resolution without saving it
|
||||
Resized();
|
||||
CompileDraw2DProgram();
|
||||
SetLineWidth();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::SetTextureCache(TextureCacheGLES *tc) {
|
||||
@@ -449,18 +448,6 @@ void FramebufferManagerGLES::RebindFramebuffer() {
|
||||
glstate.viewport.restore();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::SetLineWidth() {
|
||||
#ifndef USING_GLES2
|
||||
if (g_Config.iInternalResolution == 0) {
|
||||
glLineWidth(std::max(1, (int)(renderWidth_ / 480)));
|
||||
glPointSize(std::max(1.0f, (float)(renderWidth_ / 480.f)));
|
||||
} else {
|
||||
glLineWidth(g_Config.iInternalResolution);
|
||||
glPointSize((float)g_Config.iInternalResolution);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::ReformatFramebufferFrom(VirtualFramebuffer *vfb, GEBufferFormat old) {
|
||||
if (!useBufferedRendering_ || !vfb->fbo) {
|
||||
return;
|
||||
@@ -1091,7 +1078,16 @@ void FramebufferManagerGLES::Resized() {
|
||||
}
|
||||
|
||||
DestroyDraw2DProgram();
|
||||
SetLineWidth();
|
||||
|
||||
#ifndef USING_GLES2
|
||||
if (g_Config.iInternalResolution == 0) {
|
||||
glLineWidth(std::max(1, (int)(renderWidth_ / 480)));
|
||||
glPointSize(std::max(1.0f, (float)(renderWidth_ / 480.f)));
|
||||
} else {
|
||||
glLineWidth(g_Config.iInternalResolution);
|
||||
glPointSize((float)g_Config.iInternalResolution);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!draw2dprogram_) {
|
||||
CompileDraw2DProgram();
|
||||
|
||||
@@ -218,11 +218,15 @@ void ProcessRect(const VertexData& v0, const VertexData& v1)
|
||||
|
||||
RotateUVThrough(v0, v1, *topright, *bottomleft);
|
||||
|
||||
// Four triangles to do backfaces as well. Two of them will get backface culled.
|
||||
Rasterizer::DrawTriangle(*topleft, *topright, *bottomright);
|
||||
Rasterizer::DrawTriangle(*bottomright, *topright, *topleft);
|
||||
Rasterizer::DrawTriangle(*bottomright, *bottomleft, *topleft);
|
||||
Rasterizer::DrawTriangle(*topleft, *bottomleft, *bottomright);
|
||||
if (gstate.isModeClear()) {
|
||||
Rasterizer::ClearRectangle(v0, v1);
|
||||
} else {
|
||||
// Four triangles to do backfaces as well. Two of them will get backface culled.
|
||||
Rasterizer::DrawTriangle(*topleft, *topright, *bottomright);
|
||||
Rasterizer::DrawTriangle(*bottomright, *topright, *topleft);
|
||||
Rasterizer::DrawTriangle(*bottomright, *bottomleft, *topleft);
|
||||
Rasterizer::DrawTriangle(*topleft, *bottomleft, *bottomright);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+169
-21
@@ -1183,7 +1183,7 @@ template <bool clearMode>
|
||||
void DrawTriangleSlice(
|
||||
const VertexData& v0, const VertexData& v1, const VertexData& v2,
|
||||
int minX, int minY, int maxX, int maxY,
|
||||
int hy1, int hy2)
|
||||
bool byY, int h1, int h2)
|
||||
{
|
||||
Vec4<int> bias0 = Vec4<int>::AssignToAll(IsRightSideOrFlatBottomLine(v0.screenpos.xy(), v1.screenpos.xy(), v2.screenpos.xy()) ? -1 : 0);
|
||||
Vec4<int> bias1 = Vec4<int>::AssignToAll(IsRightSideOrFlatBottomLine(v1.screenpos.xy(), v2.screenpos.xy(), v0.screenpos.xy()) ? -1 : 0);
|
||||
@@ -1215,23 +1215,26 @@ void DrawTriangleSlice(
|
||||
TriangleEdge e1;
|
||||
TriangleEdge e2;
|
||||
|
||||
if (byY) {
|
||||
maxY = std::min(maxY, minY + h2 * 16 * 2);
|
||||
minY += h1 * 16 * 2;
|
||||
} else {
|
||||
maxX = std::min(maxX, minX + h2 * 16 * 2);
|
||||
minX += h1 * 16 * 2;
|
||||
}
|
||||
|
||||
ScreenCoords pprime(minX, minY, 0);
|
||||
Vec4<int> w0_base = e0.Start(v1.screenpos, v2.screenpos, pprime);
|
||||
Vec4<int> w1_base = e1.Start(v2.screenpos, v0.screenpos, pprime);
|
||||
Vec4<int> w2_base = e2.Start(v0.screenpos, v1.screenpos, pprime);
|
||||
|
||||
// Step forward to y1 (slice..)
|
||||
w0_base += e0.stepY * hy1;
|
||||
w1_base += e1.stepY * hy1;
|
||||
w2_base += e2.stepY * hy1;
|
||||
|
||||
// All the z values are the same, no interpolation required.
|
||||
// This is common, and when we interpolate, we lose accuracy.
|
||||
const bool flatZ = v0.screenpos.z == v1.screenpos.z && v0.screenpos.z == v2.screenpos.z;
|
||||
|
||||
Sampler::Funcs sampler = Sampler::GetFuncs();
|
||||
|
||||
for (pprime.y = minY + hy1 * 32; pprime.y < minY + hy2 * 32; pprime.y += 32,
|
||||
for (pprime.y = minY; pprime.y < maxY; pprime.y += 32,
|
||||
w0_base = e0.StepY(w0_base),
|
||||
w1_base = e1.StepY(w1_base),
|
||||
w2_base = e2.StepY(w2_base)) {
|
||||
@@ -1351,8 +1354,8 @@ void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData&
|
||||
|
||||
int minX = std::min(std::min(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) & ~0xF;
|
||||
int minY = std::min(std::min(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) & ~0xF;
|
||||
int maxX = std::max(std::max(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) & ~0xF;
|
||||
int maxY = std::max(std::max(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) & ~0xF;
|
||||
int maxX = (std::max(std::max(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) + 0xF) & ~0xF;
|
||||
int maxY = (std::max(std::max(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) + 0xF) & ~0xF;
|
||||
|
||||
DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0);
|
||||
DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0);
|
||||
@@ -1362,24 +1365,37 @@ void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData&
|
||||
maxY = std::min(maxY, (int)TransformUnit::DrawingToScreen(scissorBR).y);
|
||||
|
||||
// 32 because we do two pixels at once, and we don't want overlap.
|
||||
int range = (maxY - minY) / 32 + 1;
|
||||
if (gstate.isModeClear()) {
|
||||
if (range >= 12 && (maxX - minX) >= 24 * 16) {
|
||||
int rangeY = (maxY - minY) / 32 + 1;
|
||||
int rangeX = (maxX - minX) / 32 + 1;
|
||||
if (rangeY >= 12 && rangeX >= rangeY * 4) {
|
||||
if (gstate.isModeClear()) {
|
||||
auto bound = [&](int a, int b) -> void {
|
||||
DrawTriangleSlice<true>(v0, v1, v2, minX, minY, maxX, maxY, a, b);
|
||||
DrawTriangleSlice<true>(v0, v1, v2, minX, minY, maxX, maxY, false, a, b);
|
||||
};
|
||||
GlobalThreadPool::Loop(bound, 0, range);
|
||||
GlobalThreadPool::Loop(bound, 0, rangeX);
|
||||
} else {
|
||||
DrawTriangleSlice<true>(v0, v1, v2, minX, minY, maxX, maxY, 0, range);
|
||||
auto bound = [&](int a, int b) -> void {
|
||||
DrawTriangleSlice<false>(v0, v1, v2, minX, minY, maxX, maxY, false, a, b);
|
||||
};
|
||||
GlobalThreadPool::Loop(bound, 0, rangeX);
|
||||
}
|
||||
} else if (rangeY >= 12 && rangeX >= 12) {
|
||||
if (gstate.isModeClear()) {
|
||||
auto bound = [&](int a, int b) -> void {
|
||||
DrawTriangleSlice<true>(v0, v1, v2, minX, minY, maxX, maxY, true, a, b);
|
||||
};
|
||||
GlobalThreadPool::Loop(bound, 0, rangeY);
|
||||
} else {
|
||||
auto bound = [&](int a, int b) -> void {
|
||||
DrawTriangleSlice<false>(v0, v1, v2, minX, minY, maxX, maxY, true, a, b);
|
||||
};
|
||||
GlobalThreadPool::Loop(bound, 0, rangeY);
|
||||
}
|
||||
} else {
|
||||
if (range >= 12 && (maxX - minX) >= 24 * 16) {
|
||||
auto bound = [&](int a, int b) -> void {
|
||||
DrawTriangleSlice<false>(v0, v1, v2, minX, minY, maxX, maxY, a, b);
|
||||
};
|
||||
GlobalThreadPool::Loop(bound, 0, range);
|
||||
if (gstate.isModeClear()) {
|
||||
DrawTriangleSlice<true>(v0, v1, v2, minX, minY, maxX, maxY, true, 0, rangeY);
|
||||
} else {
|
||||
DrawTriangleSlice<false>(v0, v1, v2, minX, minY, maxX, maxY, 0, range);
|
||||
DrawTriangleSlice<false>(v0, v1, v2, minX, minY, maxX, maxY, true, 0, rangeY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1460,6 +1476,138 @@ void DrawPoint(const VertexData &v0)
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRectangle(const VertexData &v0, const VertexData &v1)
|
||||
{
|
||||
int minX = std::min(v0.screenpos.x, v1.screenpos.x) & ~0xF;
|
||||
int minY = std::min(v0.screenpos.y, v1.screenpos.y) & ~0xF;
|
||||
int maxX = (std::max(v0.screenpos.x, v1.screenpos.x) + 0xF) & ~0xF;
|
||||
int maxY = (std::max(v0.screenpos.y, v1.screenpos.y) + 0xF) & ~0xF;
|
||||
|
||||
DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0);
|
||||
DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0);
|
||||
minX = std::max(minX, (int)TransformUnit::DrawingToScreen(scissorTL).x);
|
||||
maxX = std::max(0, std::min(maxX, (int)TransformUnit::DrawingToScreen(scissorBR).x));
|
||||
minY = std::max(minY, (int)TransformUnit::DrawingToScreen(scissorTL).y);
|
||||
maxY = std::max(0, std::min(maxY, (int)TransformUnit::DrawingToScreen(scissorBR).y));
|
||||
|
||||
const int w = (maxX - minX) / 16;
|
||||
if (w <= 0)
|
||||
return;
|
||||
|
||||
if (gstate.isClearModeDepthMask()) {
|
||||
ScreenCoords pprime(minX, minY, 0);
|
||||
const u16 z = v1.screenpos.z;
|
||||
const int stride = gstate.DepthBufStride();
|
||||
|
||||
for (pprime.y = minY; pprime.y < maxY; pprime.y += 16) {
|
||||
DrawingCoords p = TransformUnit::ScreenToDrawing(pprime);
|
||||
|
||||
if ((z & 0xFF) == (z >> 8)) {
|
||||
u16 *row = &depthbuf.as16[p.x + p.y * stride];
|
||||
memset(row, z, w * 2);
|
||||
} else {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
SetPixelDepth(p.x + x, p.y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const u32 new_color = v1.color0.ToRGBA();
|
||||
u16 new_color16;
|
||||
|
||||
// Note: this stays 0xFFFFFFFF if keeping color and alpha, even for 16-bit.
|
||||
u32 keepOldMask = 0xFFFFFFFF;
|
||||
switch (gstate.FrameBufFormat()) {
|
||||
case GE_FORMAT_565:
|
||||
new_color16 = RGBA8888ToRGB565(new_color);
|
||||
if (gstate.isClearModeColorMask())
|
||||
keepOldMask = 0;
|
||||
break;
|
||||
|
||||
case GE_FORMAT_5551:
|
||||
new_color16 = RGBA8888ToRGBA5551(new_color);
|
||||
if (gstate.isClearModeColorMask())
|
||||
keepOldMask &= 0x00008000;
|
||||
if (gstate.isClearModeAlphaMask())
|
||||
keepOldMask &= 0x00007FFF;
|
||||
break;
|
||||
|
||||
case GE_FORMAT_4444:
|
||||
new_color16 = RGBA8888ToRGBA4444(new_color);
|
||||
if (gstate.isClearModeColorMask())
|
||||
keepOldMask &= 0x0000F000;
|
||||
if (gstate.isClearModeAlphaMask())
|
||||
keepOldMask &= 0x00000FFF;
|
||||
break;
|
||||
|
||||
case GE_FORMAT_8888:
|
||||
if (gstate.isClearModeColorMask())
|
||||
keepOldMask &= 0xFF000000;
|
||||
if (gstate.isClearModeAlphaMask())
|
||||
keepOldMask &= 0x00FFFFFF;
|
||||
break;
|
||||
|
||||
case GE_FORMAT_INVALID:
|
||||
_dbg_assert_msg_(G3D, false, "Software: invalid framebuf format.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (keepOldMask == 0) {
|
||||
ScreenCoords pprime(minX, minY, 0);
|
||||
const int stride = gstate.FrameBufStride();
|
||||
|
||||
if (gstate.FrameBufFormat() == GE_FORMAT_8888) {
|
||||
for (pprime.y = minY; pprime.y < maxY; pprime.y += 16) {
|
||||
DrawingCoords p = TransformUnit::ScreenToDrawing(pprime);
|
||||
if ((new_color & 0xFF) == (new_color >> 8) && (new_color && 0xFFFF) == (new_color >> 16)) {
|
||||
u32 *row = &fb.as32[p.x + p.y * stride];
|
||||
memset(row, new_color, w * 4);
|
||||
} else {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
fb.Set32(p.x + x, p.y, stride, new_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (pprime.y = minY; pprime.y < maxY; pprime.y += 16) {
|
||||
DrawingCoords p = TransformUnit::ScreenToDrawing(pprime);
|
||||
if ((new_color16 & 0xFF) == (new_color16 >> 8)) {
|
||||
u16 *row = &fb.as16[p.x + p.y * stride];
|
||||
memset(row, new_color16, w * 2);
|
||||
} else {
|
||||
for (int x = 0; x < w; ++x) {
|
||||
fb.Set16(p.x + x, p.y, stride, new_color16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (keepOldMask != 0xFFFFFFFF) {
|
||||
ScreenCoords pprime(minX, minY, 0);
|
||||
const int stride = gstate.FrameBufStride();
|
||||
|
||||
if (gstate.FrameBufFormat() == GE_FORMAT_8888) {
|
||||
for (pprime.y = minY; pprime.y < maxY; pprime.y += 16) {
|
||||
DrawingCoords p = TransformUnit::ScreenToDrawing(pprime);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
const u32 old_color = fb.Get32(p.x + x, p.y, stride);
|
||||
const u32 c = (old_color & keepOldMask) | (new_color & ~keepOldMask);
|
||||
fb.Set32(p.x + x, p.y, stride, c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (pprime.y = minY; pprime.y < maxY; pprime.y += 16) {
|
||||
DrawingCoords p = TransformUnit::ScreenToDrawing(pprime);
|
||||
for (int x = 0; x < w; ++x) {
|
||||
const u16 old_color = fb.Get16(p.x + x, p.y, stride);
|
||||
const u16 c = (old_color & keepOldMask) | (new_color16 & ~keepOldMask);
|
||||
fb.Set16(p.x + x, p.y, stride, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawLine(const VertexData &v0, const VertexData &v1)
|
||||
{
|
||||
// TODO: Use a proper line drawing algorithm that handles fractional endpoints correctly.
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Rasterizer {
|
||||
void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData& v2);
|
||||
void DrawPoint(const VertexData &v0);
|
||||
void DrawLine(const VertexData &v0, const VertexData &v1);
|
||||
void ClearRectangle(const VertexData &v0, const VertexData &v1);
|
||||
|
||||
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer);
|
||||
bool GetCurrentTexture(GPUDebugBuffer &buffer, int level);
|
||||
|
||||
@@ -155,22 +155,31 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) {
|
||||
fbTex = nullptr;
|
||||
}
|
||||
|
||||
// For accuracy, try to handle 0 stride - sometimes used.
|
||||
if (displayStride_ == 0) {
|
||||
srcheight = 1;
|
||||
}
|
||||
|
||||
Draw::TextureDesc desc{};
|
||||
desc.type = Draw::TextureType::LINEAR2D;
|
||||
desc.format = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
bool hasImage = true;
|
||||
if (!Memory::IsValidAddress(displayFramebuf_)) {
|
||||
if (!Memory::IsValidAddress(displayFramebuf_) || srcwidth == 0 || srcheight == 0) {
|
||||
hasImage = false;
|
||||
u1 = 1.0f;
|
||||
} else if (displayFormat_ == GE_FORMAT_8888) {
|
||||
u8 *data = Memory::GetPointer(displayFramebuf_);
|
||||
desc.width = displayStride_;
|
||||
desc.width = displayStride_ == 0 ? srcwidth : displayStride_;
|
||||
desc.height = srcheight;
|
||||
desc.initData.push_back(data);
|
||||
desc.format = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
u1 = (float)srcwidth / displayStride_;
|
||||
if (displayStride_ != 0) {
|
||||
u1 = (float)srcwidth / displayStride_;
|
||||
} else {
|
||||
u1 = 1.0f;
|
||||
}
|
||||
} else {
|
||||
// TODO: This should probably be converted in a shader instead..
|
||||
fbTexBuffer.resize(srcwidth * srcheight);
|
||||
|
||||
+64
-127
@@ -67,9 +67,7 @@ enum {
|
||||
DRAW_BINDING_DYNUBO_BASE = 2,
|
||||
DRAW_BINDING_DYNUBO_LIGHT = 3,
|
||||
DRAW_BINDING_DYNUBO_BONE = 4,
|
||||
DRAW_BINDING_TESS_POS_TEXTURE = 5,
|
||||
DRAW_BINDING_TESS_TEX_TEXTURE = 6,
|
||||
DRAW_BINDING_TESS_COL_TEXTURE = 7,
|
||||
DRAW_BINDING_TESS_STORAGE_BUF = 5,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -97,7 +95,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra
|
||||
|
||||
void DrawEngineVulkan::InitDeviceObjects() {
|
||||
// All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated.
|
||||
VkDescriptorSetLayoutBinding bindings[8];
|
||||
VkDescriptorSetLayoutBinding bindings[6];
|
||||
bindings[0].descriptorCount = 1;
|
||||
bindings[0].pImmutableSamplers = nullptr;
|
||||
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
@@ -123,37 +121,28 @@ void DrawEngineVulkan::InitDeviceObjects() {
|
||||
bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
bindings[4].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
bindings[4].binding = DRAW_BINDING_DYNUBO_BONE;
|
||||
// Hardware tessellation. TODO: Don't allocate these unless actually drawing splines.
|
||||
// Will require additional
|
||||
// Used only for hardware tessellation.
|
||||
bindings[5].descriptorCount = 1;
|
||||
bindings[5].pImmutableSamplers = nullptr;
|
||||
bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[5].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
bindings[5].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
bindings[5].binding = DRAW_BINDING_TESS_POS_TEXTURE;
|
||||
bindings[6].descriptorCount = 1;
|
||||
bindings[6].pImmutableSamplers = nullptr;
|
||||
bindings[6].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[6].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
bindings[6].binding = DRAW_BINDING_TESS_TEX_TEXTURE;
|
||||
bindings[7].descriptorCount = 1;
|
||||
bindings[7].pImmutableSamplers = nullptr;
|
||||
bindings[7].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[7].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
bindings[7].binding = DRAW_BINDING_TESS_COL_TEXTURE;
|
||||
bindings[5].binding = DRAW_BINDING_TESS_STORAGE_BUF;
|
||||
|
||||
VkDevice device = vulkan_->GetDevice();
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo dsl = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
dsl.bindingCount = 8;
|
||||
dsl.bindingCount = ARRAY_SIZE(bindings);
|
||||
dsl.pBindings = bindings;
|
||||
VkResult res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_);
|
||||
assert(VK_SUCCESS == res);
|
||||
|
||||
VkDescriptorPoolSize dpTypes[2];
|
||||
VkDescriptorPoolSize dpTypes[3];
|
||||
dpTypes[0].descriptorCount = 8192;
|
||||
dpTypes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
dpTypes[1].descriptorCount = 8192 + 4096; // Due to the tess stuff, we need a LOT of these. Most will be empty...
|
||||
dpTypes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
dpTypes[2].descriptorCount = 2048;
|
||||
dpTypes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
|
||||
VkDescriptorPoolCreateInfo dp = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
dp.pNext = nullptr;
|
||||
@@ -509,6 +498,10 @@ void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset,
|
||||
}
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::SetLineWidth(float lineWidth) {
|
||||
pipelineManager_->SetLineWidth(lineWidth);
|
||||
}
|
||||
|
||||
VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone) {
|
||||
DescriptorSetKey key;
|
||||
key.imageView_ = imageView;
|
||||
@@ -517,12 +510,15 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView
|
||||
key.base_ = base;
|
||||
key.light_ = light;
|
||||
key.bone_ = bone;
|
||||
assert(base != VK_NULL_HANDLE);
|
||||
assert(light != VK_NULL_HANDLE);
|
||||
assert(bone != VK_NULL_HANDLE);
|
||||
_dbg_assert_(G3D, base != VK_NULL_HANDLE);
|
||||
_dbg_assert_(G3D, light != VK_NULL_HANDLE);
|
||||
_dbg_assert_(G3D, bone != VK_NULL_HANDLE);
|
||||
|
||||
bool tess = gstate_c.bezier || gstate_c.spline;
|
||||
|
||||
FrameData *frame = &frame_[vulkan_->GetCurFrame()];
|
||||
if (!gstate_c.bezier && !gstate_c.spline) { // Has no cache when HW tessellation.
|
||||
// See if we already have this descriptor set cached.
|
||||
if (!tess) { // Don't cache descriptors for HW tessellation.
|
||||
VkDescriptorSet d = frame->descSets.Get(key);
|
||||
if (d != VK_NULL_HANDLE)
|
||||
return d;
|
||||
@@ -579,29 +575,27 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView
|
||||
|
||||
// Skipping 2nd texture for now.
|
||||
|
||||
// Tessellation data textures
|
||||
if (gstate_c.bezier || gstate_c.spline) {
|
||||
VkDescriptorImageInfo tess_tex[3]{};
|
||||
VkSampler sampler = ((TessellationDataTransferVulkan *)tessDataTransfer)->GetSampler();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
VulkanTexture *texture = ((TessellationDataTransferVulkan *)tessDataTransfer)->GetTexture(i);
|
||||
if (texture) {
|
||||
assert(texture->GetImageView());
|
||||
VkImageView imageView = texture->GetImageView();
|
||||
|
||||
tess_tex[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
tess_tex[i].imageView = imageView;
|
||||
tess_tex[i].sampler = sampler;
|
||||
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[n].pNext = nullptr;
|
||||
writes[n].dstBinding = DRAW_BINDING_TESS_POS_TEXTURE + i;
|
||||
writes[n].pImageInfo = &tess_tex[i];
|
||||
writes[n].descriptorCount = 1;
|
||||
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
writes[n].dstSet = desc;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
// Tessellation data buffer. Make sure this is declared outside the if to avoid optimizer
|
||||
// shenanigans.
|
||||
VkDescriptorBufferInfo tess_buf{};
|
||||
if (tess) {
|
||||
VkBuffer buf;
|
||||
VkDeviceSize offset;
|
||||
VkDeviceSize range;
|
||||
((TessellationDataTransferVulkan *)tessDataTransfer)->GetBufferAndOffset(&buf, &offset, &range);
|
||||
assert(buf);
|
||||
tess_buf.buffer = buf;
|
||||
tess_buf.offset = offset;
|
||||
tess_buf.range = range;
|
||||
tessOffset_ = offset;
|
||||
writes[n].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
writes[n].pNext = nullptr;
|
||||
writes[n].dstBinding = DRAW_BINDING_TESS_STORAGE_BUF;
|
||||
writes[n].pBufferInfo = &tess_buf;
|
||||
writes[n].descriptorCount = 1;
|
||||
writes[n].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
writes[n].dstSet = desc;
|
||||
n++;
|
||||
}
|
||||
|
||||
// Uniform buffer objects
|
||||
@@ -1003,7 +997,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
}
|
||||
lastPipeline_ = pipeline;
|
||||
}
|
||||
ApplyDrawStateLate(renderManager, false, 0, pipeline->useBlendConstant);
|
||||
ApplyDrawStateLate(renderManager, result.setStencil, result.stencilValue, pipeline->useBlendConstant);
|
||||
gstate_c.Clean(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
|
||||
lastPipeline_ = pipeline;
|
||||
}
|
||||
@@ -1091,97 +1085,40 @@ void DrawEngineVulkan::UpdateUBOs(FrameData *frame) {
|
||||
|
||||
DrawEngineVulkan::TessellationDataTransferVulkan::TessellationDataTransferVulkan(VulkanContext *vulkan, Draw::DrawContext *draw)
|
||||
: TessellationDataTransfer(), vulkan_(vulkan), draw_(draw) {
|
||||
CreateSampler();
|
||||
}
|
||||
|
||||
DrawEngineVulkan::TessellationDataTransferVulkan::~TessellationDataTransferVulkan() {
|
||||
for (int i = 0; i < 3; i++)
|
||||
delete data_tex[i];
|
||||
vulkan_->Delete().QueueDeleteSampler(sampler);
|
||||
}
|
||||
|
||||
// TODO: Consolidate the three textures into one, with height 3.
|
||||
// This can be done for all the backends.
|
||||
void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) {
|
||||
// TODO: Actually, even better, avoid the usage of textures altogether and just use shader storage buffers from the current pushbuffer.
|
||||
void DrawEngineVulkan::TessellationDataTransferVulkan::PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) {
|
||||
colStride = 4;
|
||||
|
||||
assert(size > 0);
|
||||
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
|
||||
// Position
|
||||
delete data_tex[0];
|
||||
data_tex[0] = new VulkanTexture(vulkan_, nullptr); // TODO: Should really use an allocator.
|
||||
bool success = data_tex[0]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
assert(success);
|
||||
// TODO: This SHOULD work without padding but I can't get it to work on nvidia, so had
|
||||
// to expand to vec4. Driver bug?
|
||||
struct TessData {
|
||||
float pos[3]; float pad1;
|
||||
float uv[2]; float pad2[2];
|
||||
float color[4];
|
||||
};
|
||||
|
||||
pos = (float *)push_->Push(size * sizeof(float) * 4, &posOffset_, &posBuf_);
|
||||
posSize_ = size;
|
||||
int ssboAlignment = vulkan_->GetPhysicalDeviceProperties().limits.minStorageBufferOffsetAlignment;
|
||||
uint8_t *data = (uint8_t *)push_->PushAligned(size * sizeof(TessData), &offset_, &buf_, ssboAlignment);
|
||||
range_ = size * sizeof(TessData);
|
||||
|
||||
// Texcoords
|
||||
delete data_tex[1];
|
||||
if (hasTexCoords) {
|
||||
data_tex[1] = new VulkanTexture(vulkan_, nullptr); // TODO: Should really use an allocator.
|
||||
success = data_tex[1]->CreateDirect(cmd, size, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
assert(success);
|
||||
|
||||
tex = (float *)push_->Push(size * sizeof(float) * 4, &texOffset_, &texBuf_);
|
||||
texSize_ = size;
|
||||
} else {
|
||||
data_tex[1] = nullptr;
|
||||
tex = nullptr;
|
||||
texSize_ = 0;
|
||||
}
|
||||
|
||||
// Color
|
||||
colSize_ = hasColor ? size : 1;
|
||||
delete data_tex[2];
|
||||
data_tex[2] = new VulkanTexture(vulkan_, nullptr); // TODO: Should really use an allocator.
|
||||
success = data_tex[2]->CreateDirect(cmd, colSize_, 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
assert(success);
|
||||
|
||||
col = (float *)push_->Push(colSize_ * sizeof(float) * 4, &colOffset_, &colBuf_);
|
||||
pos = (float *)(data);
|
||||
tex = (float *)(data + offsetof(TessData, uv));
|
||||
col = (float *)(data + offsetof(TessData, color));
|
||||
posStride = sizeof(TessData) / sizeof(float);
|
||||
colStride = hasColor ? (sizeof(TessData) / sizeof(float)) : 0;
|
||||
texStride = sizeof(TessData) / sizeof(float);
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::TessellationDataTransferVulkan::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) {
|
||||
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
|
||||
// Position
|
||||
data_tex[0]->UploadMip(cmd, 0, posSize_, 1, posBuf_, posOffset_, posSize_);
|
||||
data_tex[0]->EndCreate(cmd, true);
|
||||
|
||||
// Texcoords
|
||||
if (hasTexCoords) {
|
||||
data_tex[1]->UploadMip(cmd, 0, texSize_, 1, texBuf_, texOffset_, texSize_);
|
||||
data_tex[1]->EndCreate(cmd, true);
|
||||
}
|
||||
|
||||
// Color
|
||||
data_tex[2]->UploadMip(cmd, 0, colSize_, 1, colBuf_, colOffset_, colSize_);
|
||||
data_tex[2]->EndCreate(cmd, true);
|
||||
}
|
||||
|
||||
void DrawEngineVulkan::TessellationDataTransferVulkan::CreateSampler() {
|
||||
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
||||
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samp.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
||||
samp.compareOp = VK_COMPARE_OP_NEVER;
|
||||
samp.flags = 0;
|
||||
samp.magFilter = VK_FILTER_NEAREST;
|
||||
samp.minFilter = VK_FILTER_NEAREST;
|
||||
samp.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
|
||||
if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY) && g_Config.iAnisotropyLevel > 0) {
|
||||
// Docs say the min of this value and the supported max are used.
|
||||
samp.maxAnisotropy = 1 << g_Config.iAnisotropyLevel;
|
||||
samp.anisotropyEnable = true;
|
||||
} else {
|
||||
samp.maxAnisotropy = 1.0f;
|
||||
samp.anisotropyEnable = false;
|
||||
}
|
||||
|
||||
samp.maxLod = 1.0f;
|
||||
samp.minLod = 0.0f;
|
||||
samp.mipLodBias = 0.0f;
|
||||
|
||||
VkResult res = vkCreateSampler(vulkan_->GetDevice(), &samp, nullptr, &sampler);
|
||||
assert(res == VK_SUCCESS);
|
||||
assert(pos);
|
||||
// Nothing to do here!
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Common/Hashmaps.h"
|
||||
#include "Common/Vulkan/VulkanMemory.h"
|
||||
|
||||
#include "GPU/Vulkan/VulkanUtil.h"
|
||||
|
||||
@@ -175,6 +176,8 @@ public:
|
||||
return stats_;
|
||||
}
|
||||
|
||||
void SetLineWidth(float lineWidth);
|
||||
|
||||
private:
|
||||
struct FrameData;
|
||||
void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant);
|
||||
@@ -259,6 +262,8 @@ private:
|
||||
VulkanPipelineRasterStateKey pipelineKey_{};
|
||||
VulkanDynamicState dynState_{};
|
||||
|
||||
int tessOffset_ = 0;
|
||||
|
||||
// Hardware tessellation
|
||||
class TessellationDataTransferVulkan : public TessellationDataTransfer {
|
||||
public:
|
||||
@@ -267,28 +272,26 @@ private:
|
||||
|
||||
void SetPushBuffer(VulkanPushBuffer *push) { push_ = push; }
|
||||
void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override;
|
||||
void PrepareBuffers(float *&pos, float *&tex, float *&col, int size, bool hasColor, bool hasTexCoords) override;
|
||||
void PrepareBuffers(float *&pos, float *&tex, float *&col, int &posStride, int &texStride, int &colStride, int size, bool hasColor, bool hasTexCoords) override;
|
||||
|
||||
VulkanTexture *GetTexture(int i) const { return data_tex[i]; }
|
||||
void GetBufferAndOffset(VkBuffer *buf, VkDeviceSize *offset, VkDeviceSize *range) {
|
||||
*buf = buf_;
|
||||
*offset = (VkDeviceSize)offset_;
|
||||
*range = (VkDeviceSize)range_;
|
||||
|
||||
buf_ = 0;
|
||||
offset_ = 0;
|
||||
range_ = 0;
|
||||
}
|
||||
|
||||
VkSampler GetSampler() const { return sampler; }
|
||||
void CreateSampler();
|
||||
private:
|
||||
VulkanContext *vulkan_;
|
||||
Draw::DrawContext *draw_;
|
||||
VulkanTexture *data_tex[3]{};
|
||||
VkSampler sampler = VK_NULL_HANDLE;
|
||||
VulkanPushBuffer *push_; // Updated each frame.
|
||||
|
||||
int posSize_ = 0;
|
||||
uint32_t posOffset_ = 0;
|
||||
VkBuffer posBuf_ = 0;
|
||||
int texSize_ = 0;
|
||||
uint32_t texOffset_ = 0;
|
||||
VkBuffer texBuf_ = 0;
|
||||
int colSize_ = 0;
|
||||
uint32_t colOffset_ = 0;
|
||||
VkBuffer colBuf_ = 0;
|
||||
|
||||
int size_ = 0;
|
||||
uint32_t offset_ = 0;
|
||||
uint32_t range_ = 0;
|
||||
VkBuffer buf_ = VK_NULL_HANDLE;
|
||||
};
|
||||
};
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "base/timeutil.h"
|
||||
#include "math/lin/matrix4x4.h"
|
||||
#include "math/dataconv.h"
|
||||
#include "i18n/i18n.h"
|
||||
#include "ext/native/file/vfs.h"
|
||||
#include "ext/native/thin3d/thin3d.h"
|
||||
|
||||
#include "Common/Vulkan/VulkanContext.h"
|
||||
@@ -39,6 +41,7 @@
|
||||
#include "GPU/ge_constants.h"
|
||||
#include "GPU/GPUState.h"
|
||||
|
||||
#include "GPU/Common/ShaderTranslation.h"
|
||||
#include "GPU/Common/PostShader.h"
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
#include "GPU/Common/FramebufferCommon.h"
|
||||
@@ -155,6 +158,12 @@ void FramebufferManagerVulkan::DestroyDeviceObjects() {
|
||||
vulkan_->Delete().QueueDeleteSampler(linearSampler_);
|
||||
if (nearestSampler_ != VK_NULL_HANDLE)
|
||||
vulkan_->Delete().QueueDeleteSampler(nearestSampler_);
|
||||
|
||||
if (postVs_)
|
||||
vulkan_->Delete().QueueDeleteShaderModule(postVs_);
|
||||
if (postFs_)
|
||||
vulkan_->Delete().QueueDeleteShaderModule(postFs_);
|
||||
pipelinePostShader_ = VK_NULL_HANDLE; // actual pipeline should get destroyed by vulkan2d.
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) {
|
||||
@@ -180,28 +189,6 @@ void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, boo
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight) {
|
||||
float u_delta = 1.0f / renderWidth;
|
||||
float v_delta = 1.0f / renderHeight;
|
||||
float u_pixel_delta = u_delta;
|
||||
float v_pixel_delta = v_delta;
|
||||
if (postShaderAtOutputResolution_) {
|
||||
float x, y, w, h;
|
||||
CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
|
||||
u_pixel_delta = (1.0f / w) * (480.0f / bufferWidth);
|
||||
v_pixel_delta = (1.0f / h) * (272.0f / bufferHeight);
|
||||
}
|
||||
|
||||
postUniforms_.texelDelta[0] = u_delta;
|
||||
postUniforms_.texelDelta[1] = v_delta;
|
||||
postUniforms_.pixelDelta[0] = u_pixel_delta;
|
||||
postUniforms_.pixelDelta[1] = v_pixel_delta;
|
||||
int flipCount = __DisplayGetFlipCount();
|
||||
int vCount = __DisplayGetVCount();
|
||||
float time[4] = { time_now(), (vCount % 60) * 1.0f / 60.0f, (float)vCount, (float)(flipCount % 60) };
|
||||
memcpy(postUniforms_.time, time, 4 * sizeof(float));
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::Init() {
|
||||
FramebufferManagerCommon::Init();
|
||||
// Workaround for upscaling shaders where we force x1 resolution without saving it
|
||||
@@ -349,6 +336,9 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa
|
||||
VkBuffer vbuffer;
|
||||
VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer);
|
||||
renderManager->BindPipeline(cur2DPipeline_);
|
||||
if (cur2DPipeline_ == pipelinePostShader_) {
|
||||
renderManager->PushConstants(vulkan2D_->GetPipelineLayout(), VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT, 0, (int)sizeof(postShaderUniforms_), &postShaderUniforms_);
|
||||
}
|
||||
renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4);
|
||||
}
|
||||
|
||||
@@ -358,7 +348,22 @@ void FramebufferManagerVulkan::Bind2DShader() {
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::BindPostShader(const PostShaderUniforms &uniforms) {
|
||||
Bind2DShader();
|
||||
if (!pipelinePostShader_) {
|
||||
if (usePostShader_) {
|
||||
CompilePostShader();
|
||||
}
|
||||
if (!usePostShader_) {
|
||||
SetNumExtraFBOs(0);
|
||||
Bind2DShader();
|
||||
return;
|
||||
} else {
|
||||
SetNumExtraFBOs(1);
|
||||
}
|
||||
}
|
||||
|
||||
postShaderUniforms_ = uniforms;
|
||||
cur2DPipeline_ = pipelinePostShader_;
|
||||
|
||||
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
|
||||
}
|
||||
|
||||
@@ -462,7 +467,7 @@ VkImageView FramebufferManagerVulkan::BindFramebufferAsColorTexture(int stage, V
|
||||
bool FramebufferManagerVulkan::CreateDownloadTempBuffer(VirtualFramebuffer *nvfb) {
|
||||
nvfb->colorDepth = Draw::FBO_8888;
|
||||
|
||||
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->width, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth });
|
||||
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->height, 1, 1, true, (Draw::FBColorDepth)nvfb->colorDepth });
|
||||
if (!(nvfb->fbo)) {
|
||||
ERROR_LOG(FRAMEBUF, "Error creating FBO! %i x %i", nvfb->renderWidth, nvfb->renderHeight);
|
||||
return false;
|
||||
@@ -626,4 +631,72 @@ void FramebufferManagerVulkan::Resized() {
|
||||
if (UpdateSize()) {
|
||||
DestroyAllFBOs();
|
||||
}
|
||||
|
||||
// Might have a new post shader - let's compile it.
|
||||
CompilePostShader();
|
||||
}
|
||||
|
||||
void FramebufferManagerVulkan::CompilePostShader() {
|
||||
if (postVs_) {
|
||||
vulkan_->Delete().QueueDeleteShaderModule(postVs_);
|
||||
}
|
||||
if (postFs_) {
|
||||
vulkan_->Delete().QueueDeleteShaderModule(postFs_);
|
||||
}
|
||||
|
||||
const ShaderInfo *shaderInfo = nullptr;
|
||||
if (g_Config.sPostShaderName == "Off") {
|
||||
usePostShader_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
usePostShader_ = false;
|
||||
|
||||
ReloadAllPostShaderInfo();
|
||||
shaderInfo = GetPostShaderInfo(g_Config.sPostShaderName);
|
||||
std::string errorVSX, errorFSX;
|
||||
std::string vsSource;
|
||||
std::string fsSource;
|
||||
if (shaderInfo) {
|
||||
postShaderAtOutputResolution_ = shaderInfo->outputResolution;
|
||||
size_t sz;
|
||||
char *vs = (char *)VFSReadFile(shaderInfo->vertexShaderFile.c_str(), &sz);
|
||||
if (!vs)
|
||||
return;
|
||||
char *fs = (char *)VFSReadFile(shaderInfo->fragmentShaderFile.c_str(), &sz);
|
||||
if (!fs) {
|
||||
free(vs);
|
||||
return;
|
||||
}
|
||||
std::string vsSourceGLSL = vs;
|
||||
std::string fsSourceGLSL = fs;
|
||||
free(vs);
|
||||
free(fs);
|
||||
TranslatedShaderMetadata metaVS, metaFS;
|
||||
if (!TranslateShader(&vsSource, GLSL_VULKAN, &metaVS, vsSourceGLSL, GLSL_140, Draw::ShaderStage::VERTEX, &errorVSX))
|
||||
return;
|
||||
if (!TranslateShader(&fsSource, GLSL_VULKAN, &metaFS, fsSourceGLSL, GLSL_140, Draw::ShaderStage::FRAGMENT, &errorFSX))
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
I18NCategory *gr = GetI18NCategory("Graphics");
|
||||
|
||||
// TODO: Delete the old pipeline?
|
||||
|
||||
std::string errorVS;
|
||||
std::string errorFS;
|
||||
postVs_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, vsSource.c_str(), &errorVS);
|
||||
postFs_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, fsSource.c_str(), &errorFS);
|
||||
|
||||
VkRenderPass backbufferRP = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS);
|
||||
|
||||
if (postVs_ && postFs_) {
|
||||
pipelinePostShader_ = vulkan2D_->GetPipeline(backbufferRP, postVs_, postFs_, true, Vulkan2D::VK2DDepthStencilMode::NONE);
|
||||
} else {
|
||||
ELOG("Failed to compile.");
|
||||
}
|
||||
|
||||
|
||||
usePostShader_ = true;
|
||||
}
|
||||
@@ -86,6 +86,7 @@ public:
|
||||
void NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth);
|
||||
|
||||
protected:
|
||||
void CompilePostShader();
|
||||
void Bind2DShader() override;
|
||||
void BindPostShader(const PostShaderUniforms &uniforms) override;
|
||||
void SetViewport2D(int x, int y, int w, int h) override;
|
||||
@@ -100,8 +101,6 @@ private:
|
||||
// The returned texture does not need to be free'd, might be returned from a pool (currently single entry)
|
||||
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
|
||||
|
||||
void UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight);
|
||||
|
||||
void InitDeviceObjects();
|
||||
void DestroyDeviceObjects();
|
||||
|
||||
@@ -130,16 +129,20 @@ private:
|
||||
VkPipelineCache pipelineCache2D_;
|
||||
|
||||
// Basic shaders
|
||||
VkShaderModule fsBasicTex_;
|
||||
VkShaderModule vsBasicTex_;
|
||||
VkShaderModule fsBasicTex_ = VK_NULL_HANDLE;
|
||||
VkShaderModule vsBasicTex_ = VK_NULL_HANDLE;
|
||||
|
||||
VkShaderModule stencilVs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule stencilFs_ = VK_NULL_HANDLE;
|
||||
|
||||
|
||||
VkPipeline cur2DPipeline_ = VK_NULL_HANDLE;
|
||||
|
||||
// Postprocessing
|
||||
VkPipeline pipelinePostShader_;
|
||||
VkShaderModule postVs_ = VK_NULL_HANDLE;
|
||||
VkShaderModule postFs_ = VK_NULL_HANDLE;
|
||||
VkPipeline pipelinePostShader_ = VK_NULL_HANDLE;
|
||||
PostShaderUniforms postShaderUniforms_;
|
||||
|
||||
VkSampler linearSampler_;
|
||||
VkSampler nearestSampler_;
|
||||
|
||||
@@ -95,11 +95,11 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
drawEngine_.SetFramebufferManager(framebufferManagerVulkan_);
|
||||
drawEngine_.SetShaderManager(shaderManagerVulkan_);
|
||||
drawEngine_.SetPipelineManager(pipelineManager_);
|
||||
framebufferManagerVulkan_->SetVulkan2D(&vulkan2D_);
|
||||
framebufferManagerVulkan_->Init();
|
||||
framebufferManagerVulkan_->SetTextureCache(textureCacheVulkan_);
|
||||
framebufferManagerVulkan_->SetDrawEngine(&drawEngine_);
|
||||
framebufferManagerVulkan_->SetShaderManager(shaderManagerVulkan_);
|
||||
framebufferManagerVulkan_->SetVulkan2D(&vulkan2D_);
|
||||
textureCacheVulkan_->SetDepalShaderCache(&depalShaderCache_);
|
||||
textureCacheVulkan_->SetFramebufferManager(framebufferManagerVulkan_);
|
||||
textureCacheVulkan_->SetShaderManager(shaderManagerVulkan_);
|
||||
@@ -158,6 +158,9 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
UpdateVsyncInterval(true);
|
||||
|
||||
textureCacheVulkan_->NotifyConfigChanged();
|
||||
if (vulkan_->GetFeaturesEnabled().wideLines) {
|
||||
drawEngine_.SetLineWidth(PSP_CoreParameter().renderWidth / 480.0f);
|
||||
}
|
||||
}
|
||||
|
||||
GPU_Vulkan::~GPU_Vulkan() {
|
||||
@@ -221,6 +224,9 @@ void GPU_Vulkan::BeginHostFrame() {
|
||||
framebufferManager_->Resized();
|
||||
drawEngine_.Resized();
|
||||
textureCacheVulkan_->NotifyConfigChanged();
|
||||
if (vulkan_->GetFeaturesEnabled ().wideLines) {
|
||||
drawEngine_.SetLineWidth(PSP_CoreParameter().renderWidth / 480.0f);
|
||||
}
|
||||
}
|
||||
resized_ = false;
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ static bool UsesBlendConstant(int factor) {
|
||||
|
||||
static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pipelineCache,
|
||||
VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key,
|
||||
const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
|
||||
const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, float lineWidth) {
|
||||
bool useBlendConstant = false;
|
||||
|
||||
VkPipelineColorBlendAttachmentState blend0 = {};
|
||||
@@ -188,7 +188,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
|
||||
rs.depthBiasEnable = false;
|
||||
rs.cullMode = key.cullMode;
|
||||
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rs.lineWidth = 1.0f;
|
||||
rs.lineWidth = lineWidth;
|
||||
rs.rasterizerDiscardEnable = false;
|
||||
rs.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
rs.depthClampEnable = false;
|
||||
@@ -289,6 +289,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
|
||||
vulkanPipeline->pipeline = pipeline;
|
||||
vulkanPipeline->uniformBlocks = UB_VS_FS_BASE;
|
||||
vulkanPipeline->useBlendConstant = useBlendConstant;
|
||||
vulkanPipeline->usesLines = key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_LIST || key.topology == VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
|
||||
if (useHwTransform) {
|
||||
if (vs->HasLights()) {
|
||||
vulkanPipeline->uniformBlocks |= UB_VS_LIGHTS;
|
||||
@@ -320,7 +321,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layo
|
||||
|
||||
VulkanPipeline *pipeline = CreateVulkanPipeline(
|
||||
vulkan_->GetDevice(), pipelineCache_, layout, renderPass,
|
||||
rasterKey, vtxDec, vs, fs, useHwTransform);
|
||||
rasterKey, vtxDec, vs, fs, useHwTransform, lineWidth_);
|
||||
// Even if the result is nullptr, insert it so we don't try to create it repeatedly.
|
||||
pipelines_.Insert(key, pipeline);
|
||||
return pipeline;
|
||||
@@ -370,3 +371,18 @@ std::string PipelineManagerVulkan::DebugGetObjectString(std::string id, DebugSha
|
||||
return "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
void PipelineManagerVulkan::SetLineWidth(float lineWidth) {
|
||||
if (lineWidth_ == lineWidth)
|
||||
return;
|
||||
lineWidth_ = lineWidth;
|
||||
|
||||
// Wipe all line-drawing pipelines.
|
||||
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
|
||||
if (value->usesLines) {
|
||||
vulkan_->Delete().QueueDeletePipeline(value->pipeline);
|
||||
delete value;
|
||||
pipelines_.Remove(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -65,6 +65,7 @@ struct VulkanPipeline {
|
||||
VkPipeline pipeline;
|
||||
int uniformBlocks; // UB_ enum above.
|
||||
bool useBlendConstant;
|
||||
bool usesLines;
|
||||
};
|
||||
|
||||
class VulkanContext;
|
||||
@@ -81,6 +82,8 @@ public:
|
||||
|
||||
void Clear();
|
||||
|
||||
void SetLineWidth(float lw);
|
||||
|
||||
void DeviceLost();
|
||||
void DeviceRestore(VulkanContext *vulkan);
|
||||
|
||||
@@ -91,4 +94,5 @@ private:
|
||||
DenseHashMap<VulkanPipelineKey, VulkanPipeline *, nullptr> pipelines_;
|
||||
VkPipelineCache pipelineCache_;
|
||||
VulkanContext *vulkan_;
|
||||
float lineWidth_ = 1.0f;
|
||||
};
|
||||
@@ -38,7 +38,7 @@
|
||||
#include "GPU/Vulkan/ShaderManagerVulkan.h"
|
||||
|
||||
static const char *vulkan_glsl_preamble =
|
||||
"#version 400\n"
|
||||
"#version 430\n"
|
||||
"#extension GL_ARB_separate_shader_objects : enable\n"
|
||||
"#extension GL_ARB_shading_language_420pack : enable\n\n";
|
||||
|
||||
@@ -224,9 +224,14 @@ bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer, bool *uses
|
||||
WRITE(p, "out gl_PerVertex { vec4 gl_Position; };\n");
|
||||
|
||||
if (doBezier || doSpline) {
|
||||
WRITE(p, "layout (binding = 5) uniform sampler2D u_tess_pos_tex;\n");
|
||||
WRITE(p, "layout (binding = 6) uniform sampler2D u_tess_tex_tex;\n");
|
||||
WRITE(p, "layout (binding = 7) uniform sampler2D u_tess_col_tex;\n");
|
||||
WRITE(p, "layout (std430) struct TessData {\n");
|
||||
WRITE(p, " vec4 pos;\n");
|
||||
WRITE(p, " vec4 uv;\n");
|
||||
WRITE(p, " vec4 color;\n");
|
||||
WRITE(p, "};");
|
||||
WRITE(p, "layout (std430, set = 0, binding = 5) buffer s_tess_data {\n");
|
||||
WRITE(p, " TessData data[];");
|
||||
WRITE(p, "} tess_data;");
|
||||
|
||||
for (int i = 2; i <= 4; i++) {
|
||||
// Define 3 types vec2, vec3, vec4
|
||||
@@ -347,12 +352,11 @@ bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer, bool *uses
|
||||
WRITE(p, " for (int i = 0; i < 4; i++) {\n");
|
||||
WRITE(p, " for (int j = 0; j < 4; j++) {\n");
|
||||
WRITE(p, " int idx = (i + v%s) * base.spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : "");
|
||||
WRITE(p, " ivec2 index = ivec2(idx, 0);\n");
|
||||
WRITE(p, " _pos[i * 4 + j] = texelFetch(u_tess_pos_tex, index, 0).xyz;\n");
|
||||
WRITE(p, " _pos[i * 4 + j] = tess_data.data[idx].pos.xyz;\n");
|
||||
if (doTexture && hasTexcoord && hasTexcoordTess)
|
||||
WRITE(p, " _tex[i * 4 + j] = texelFetch(u_tess_tex_tex, index, 0).xy;\n");
|
||||
WRITE(p, " _tex[i * 4 + j] = tess_data.data[idx].uv.xy;\n");
|
||||
if (hasColor && hasColorTess)
|
||||
WRITE(p, " _col[i * 4 + j] = texelFetch(u_tess_col_tex, index, 0).rgba;\n");
|
||||
WRITE(p, " _col[i * 4 + j] = tess_data.data[idx].color;\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " }\n");
|
||||
WRITE(p, " vec2 tess_pos = position.xy;\n");
|
||||
@@ -381,7 +385,7 @@ bool GenerateVulkanGLSLVertexShader(const ShaderID &id, char *buffer, bool *uses
|
||||
if (hasColorTess)
|
||||
WRITE(p, " vec4 col = tess_sample(_col, weights);\n");
|
||||
else
|
||||
WRITE(p, " vec4 col = texelFetch(u_tess_col_tex, ivec2(0, 0), 0).rgba;\n");
|
||||
WRITE(p, " vec4 col = tess_data.data[0].color;\n");
|
||||
}
|
||||
if (hasNormal) {
|
||||
// Curved surface is probably always need to compute normal(not sampling from control points)
|
||||
|
||||
@@ -63,12 +63,13 @@ void Vulkan2D::DestroyDeviceObjects() {
|
||||
|
||||
void Vulkan2D::InitDeviceObjects() {
|
||||
pipelineCache_ = vulkan_->CreatePipelineCache();
|
||||
// All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated.
|
||||
VkDescriptorSetLayoutBinding bindings[2] = {};
|
||||
// Texture.
|
||||
bindings[0].descriptorCount = 1;
|
||||
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
bindings[0].binding = 0;
|
||||
// In depal, this second texture is used for the palette.
|
||||
bindings[1].descriptorCount = 1;
|
||||
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
bindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
@@ -98,8 +99,8 @@ void Vulkan2D::InitDeviceObjects() {
|
||||
|
||||
VkPushConstantRange push = {};
|
||||
push.offset = 0;
|
||||
push.size = 16;
|
||||
push.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
push.size = 48;
|
||||
push.stageFlags = VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
|
||||
VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
pl.pPushConstantRanges = &push;
|
||||
|
||||
@@ -104,7 +104,7 @@ private:
|
||||
VK2DDepthStencilMode depthStencilMode;
|
||||
bool readVertices;
|
||||
bool operator < (const PipelineKey &other) const {
|
||||
return std::tie(vs, fs, rp, depthStencilMode, readVertices) < std::tie(other.vs, other.fs, other.rp, depthStencilMode, readVertices);
|
||||
return std::tie(vs, fs, rp, depthStencilMode, readVertices) < std::tie(other.vs, other.fs, other.rp, other.depthStencilMode, other.readVertices);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+12
-3
@@ -988,7 +988,11 @@ void EmuScreen::preRender() {
|
||||
bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE;
|
||||
if ((!useBufferedRendering && !g_Config.bSoftwareRendering) || Core_IsStepping()) {
|
||||
// We need to clear here already so that drawing during the frame is done on a clean slate.
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 });
|
||||
if (Core_IsStepping()) {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE });
|
||||
} else {
|
||||
draw->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, 0xFF000000 });
|
||||
}
|
||||
|
||||
Viewport viewport;
|
||||
viewport.TopLeftX = 0;
|
||||
@@ -1052,6 +1056,9 @@ void EmuScreen::render() {
|
||||
if (coreState == CORE_NEXTFRAME) {
|
||||
// set back to running for the next frame
|
||||
coreState = CORE_RUNNING;
|
||||
} else if (coreState == CORE_STEPPING) {
|
||||
// If we're stepping, it's convenient not to clear the screen.
|
||||
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE });
|
||||
} else {
|
||||
// Didn't actually reach the end of the frame, ran out of the blockTicks cycles.
|
||||
// In this case we need to bind and wipe the backbuffer, at least.
|
||||
@@ -1063,8 +1070,10 @@ void EmuScreen::render() {
|
||||
PSP_EndHostFrame();
|
||||
if (invalid_)
|
||||
return;
|
||||
|
||||
if (!osm.IsEmpty() || g_Config.bShowDebugStats || g_Config.iShowFPSCounter || g_Config.bShowTouchControls || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || saveStatePreview_->GetVisibility() != UI::V_GONE || g_Config.bShowFrameProfiler) {
|
||||
|
||||
const bool hasVisibleUI = !osm.IsEmpty() || saveStatePreview_->GetVisibility() != UI::V_GONE || g_Config.bShowTouchControls;
|
||||
const bool showDebugUI = g_Config.bShowDebugStats || g_Config.bShowDeveloperMenu || g_Config.bShowAudioDebug || g_Config.bShowFrameProfiler;
|
||||
if (hasVisibleUI || showDebugUI || g_Config.iShowFPSCounter != 0) {
|
||||
// This sets up some important states but not the viewport.
|
||||
screenManager()->getUIContext()->Begin();
|
||||
|
||||
|
||||
@@ -223,6 +223,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/GPU/Common/TextureCacheCommon.cpp.arm \
|
||||
$(SRC)/GPU/Common/TextureScalerCommon.cpp.arm \
|
||||
$(SRC)/GPU/Common/ShaderCommon.cpp \
|
||||
$(SRC)/GPU/Common/ShaderTranslation.cpp \
|
||||
$(SRC)/GPU/Common/StencilCommon.cpp \
|
||||
$(SRC)/GPU/Common/SplineCommon.cpp.arm \
|
||||
$(SRC)/GPU/Common/DrawEngineCommon.cpp.arm \
|
||||
|
||||
@@ -89,6 +89,8 @@ const char *GetFn(const char *fn);
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define DUMPLOG(x) OutputDebugStringUTF8(x)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define DLOG(...) XLOG_IMPL("D", __VA_ARGS__)
|
||||
#else
|
||||
|
||||
@@ -4,19 +4,29 @@
|
||||
|
||||
// Debug help: adb logcat -s DEBUG PPSSPPNativeActivity PPSSPP
|
||||
|
||||
// TODO: This is only enough for 4x render resolution / 4x texture upscale for debugger.
|
||||
// Maybe we should use a dynamically allocated one for larger?
|
||||
const uint32_t readbackBufferSize = 2048 * 2048 * 4;
|
||||
|
||||
void VulkanQueueRunner::CreateDeviceObjects() {
|
||||
ILOG("VulkanQueueRunner::CreateDeviceObjects");
|
||||
InitBackbufferRenderPass();
|
||||
InitRenderpasses();
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::ResizeReadbackBuffer(VkDeviceSize requiredSize) {
|
||||
if (readbackBuffer_ && requiredSize <= readbackBufferSize_) {
|
||||
return;
|
||||
}
|
||||
if (readbackMemory_) {
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(readbackMemory_);
|
||||
}
|
||||
if (readbackBuffer_) {
|
||||
vulkan_->Delete().QueueDeleteBuffer(readbackBuffer_);
|
||||
}
|
||||
|
||||
readbackBufferSize_ = requiredSize;
|
||||
|
||||
VkDevice device = vulkan_->GetDevice();
|
||||
|
||||
VkBufferCreateInfo buf{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
buf.size = readbackBufferSize;
|
||||
buf.size = readbackBufferSize_;
|
||||
buf.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
||||
|
||||
vkCreateBuffer(device, &buf, nullptr, &readbackBuffer_);
|
||||
@@ -40,10 +50,8 @@ void VulkanQueueRunner::DestroyDeviceObjects() {
|
||||
ILOG("VulkanQueueRunner::DestroyDeviceObjects");
|
||||
VkDevice device = vulkan_->GetDevice();
|
||||
vulkan_->Delete().QueueDeleteDeviceMemory(readbackMemory_);
|
||||
vkFreeMemory(device, readbackMemory_, nullptr);
|
||||
readbackMemory_ = VK_NULL_HANDLE;
|
||||
vulkan_->Delete().QueueDeleteBuffer(readbackBuffer_);
|
||||
readbackBuffer_ = VK_NULL_HANDLE;
|
||||
readbackBufferSize_ = 0;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(renderPasses_); i++) {
|
||||
assert(renderPasses_[i] != VK_NULL_HANDLE);
|
||||
@@ -845,6 +853,8 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
|
||||
vkCmdPipelineBarrier(cmd, stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
|
||||
ResizeReadbackBuffer(sizeof(uint32_t) * step.readback.srcRect.extent.width * step.readback.srcRect.extent.height);
|
||||
|
||||
VkBufferImageCopy region{};
|
||||
region.imageOffset = { step.readback.srcRect.offset.x, step.readback.srcRect.offset.y, 0 };
|
||||
region.imageExtent = { step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, 1 };
|
||||
@@ -869,6 +879,8 @@ void VulkanQueueRunner::PerformReadbackImage(const VKRStep &step, VkCommandBuffe
|
||||
SetupTransitionToTransferSrc(srcImage, barrier, stage, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
vkCmdPipelineBarrier(cmd, stage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
|
||||
ResizeReadbackBuffer(sizeof(uint32_t) * step.readback_image.srcRect.extent.width * step.readback_image.srcRect.extent.height);
|
||||
|
||||
VkBufferImageCopy region{};
|
||||
region.imageOffset = { step.readback_image.srcRect.offset.x, step.readback_image.srcRect.offset.y, 0 };
|
||||
region.imageExtent = { step.readback_image.srcRect.extent.width, step.readback_image.srcRect.extent.height, 1 };
|
||||
|
||||
@@ -74,7 +74,7 @@ struct VkRenderData {
|
||||
VkShaderStageFlags stages;
|
||||
uint8_t offset;
|
||||
uint8_t size;
|
||||
uint8_t data[32]; // Should be enough for now.
|
||||
uint8_t data[40]; // Should be enough for now.
|
||||
} push;
|
||||
};
|
||||
};
|
||||
@@ -184,6 +184,8 @@ private:
|
||||
void LogReadback(const VKRStep &pass);
|
||||
void LogReadbackImage(const VKRStep &pass);
|
||||
|
||||
void ResizeReadbackBuffer(VkDeviceSize requiredSize);
|
||||
|
||||
static void SetupTransitionToTransferSrc(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect);
|
||||
static void SetupTransitionToTransferDst(VKRImage &img, VkImageMemoryBarrier &barrier, VkPipelineStageFlags &stage, VkImageAspectFlags aspect);
|
||||
|
||||
@@ -199,6 +201,7 @@ private:
|
||||
|
||||
// Readback buffer. Currently we only support synchronous readback, so we only really need one.
|
||||
// We size it generously.
|
||||
VkDeviceMemory readbackMemory_;
|
||||
VkBuffer readbackBuffer_;
|
||||
VkDeviceMemory readbackMemory_ = VK_NULL_HANDLE;
|
||||
VkBuffer readbackBuffer_ = VK_NULL_HANDLE;
|
||||
VkDeviceSize readbackBufferSize_ = 0;
|
||||
};
|
||||
@@ -139,7 +139,7 @@ public:
|
||||
|
||||
void PushConstants(VkPipelineLayout pipelineLayout, VkShaderStageFlags stages, int offset, int size, void *constants) {
|
||||
_dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
assert(size + offset < 32);
|
||||
assert(size + offset < 40);
|
||||
VkRenderData data{ VKRRenderCommand::PUSH_CONSTANTS };
|
||||
data.push.pipelineLayout = pipelineLayout;
|
||||
data.push.stages = stages;
|
||||
|
||||
@@ -312,8 +312,9 @@ struct DescriptorSetKey {
|
||||
class VKTexture : public Texture {
|
||||
public:
|
||||
VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, const TextureDesc &desc)
|
||||
: vulkan_(vulkan), format_(desc.format), mipLevels_(desc.mipLevels) {
|
||||
Create(cmd, desc);
|
||||
: vulkan_(vulkan), mipLevels_(desc.mipLevels), format_(desc.format) {
|
||||
bool result = Create(cmd, desc);
|
||||
assert(result);
|
||||
}
|
||||
|
||||
~VKTexture() {
|
||||
@@ -336,7 +337,7 @@ private:
|
||||
}
|
||||
|
||||
VulkanContext *vulkan_;
|
||||
VulkanTexture *vkTex_;
|
||||
VulkanTexture *vkTex_ = nullptr;
|
||||
|
||||
int mipLevels_;
|
||||
|
||||
|
||||
Reference in new issue
Block a user