diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e8992b23f..8fac30bc8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Common/Vulkan/VulkanContext.h b/Common/Vulkan/VulkanContext.h index 07f82ead75..6dbef6e33b 100644 --- a/Common/Vulkan/VulkanContext.h +++ b/Common/Vulkan/VulkanContext.h @@ -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 cmdQueue_; }; diff --git a/Common/Vulkan/VulkanMemory.cpp b/Common/Vulkan/VulkanMemory.cpp index 57b219b472..f2c37c173d 100644 --- a/Common/Vulkan/VulkanMemory.cpp +++ b/Common/Vulkan/VulkanMemory.cpp @@ -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; diff --git a/Common/Vulkan/VulkanMemory.h b/Common/Vulkan/VulkanMemory.h index 57a2fee6c1..96f5833340 100644 --- a/Common/Vulkan/VulkanMemory.h +++ b/Common/Vulkan/VulkanMemory.h @@ -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; diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index cdaa579a19..fe40686ef4 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -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; }; diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index 75224a0cc5..0b1a4125aa 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -69,7 +69,7 @@ class VulkanFBO; struct PostShaderUniforms { float texelDelta[2]; float pixelDelta[2]; float time[4]; - bool video; + float video; }; struct VirtualFramebuffer { diff --git a/GPU/Common/ShaderTranslation.cpp b/GPU/Common/ShaderTranslation.cpp index 512f5201b0..1a50efbaba 100644 --- a/GPU/Common/ShaderTranslation.cpp +++ b/GPU/Common/ShaderTranslation.cpp @@ -17,8 +17,6 @@ #include "ppsspp_config.h" -#if !defined(ANDROID) - #include #include #include @@ -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 +} diff --git a/GPU/Common/ShaderTranslation.h b/GPU/Common/ShaderTranslation.h index f382f4694a..5985623661 100644 --- a/GPU/Common/ShaderTranslation.h +++ b/GPU/Common/ShaderTranslation.h @@ -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 \ No newline at end of file diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 39bff10e63..8e428b695e 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -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); diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 2a84c030ba..90b1781120 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -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(); diff --git a/GPU/Software/Clipper.cpp b/GPU/Software/Clipper.cpp index deb78c5430..72859b71c0 100644 --- a/GPU/Software/Clipper.cpp +++ b/GPU/Software/Clipper.cpp @@ -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); + } } } diff --git a/GPU/Software/Rasterizer.cpp b/GPU/Software/Rasterizer.cpp index fe9fe43266..f6185b6328 100644 --- a/GPU/Software/Rasterizer.cpp +++ b/GPU/Software/Rasterizer.cpp @@ -1183,7 +1183,7 @@ template 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 bias0 = Vec4::AssignToAll(IsRightSideOrFlatBottomLine(v0.screenpos.xy(), v1.screenpos.xy(), v2.screenpos.xy()) ? -1 : 0); Vec4 bias1 = Vec4::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 w0_base = e0.Start(v1.screenpos, v2.screenpos, pprime); Vec4 w1_base = e1.Start(v2.screenpos, v0.screenpos, pprime); Vec4 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(v0, v1, v2, minX, minY, maxX, maxY, a, b); + DrawTriangleSlice(v0, v1, v2, minX, minY, maxX, maxY, false, a, b); }; - GlobalThreadPool::Loop(bound, 0, range); + GlobalThreadPool::Loop(bound, 0, rangeX); } else { - DrawTriangleSlice(v0, v1, v2, minX, minY, maxX, maxY, 0, range); + auto bound = [&](int a, int b) -> void { + DrawTriangleSlice(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(v0, v1, v2, minX, minY, maxX, maxY, true, a, b); + }; + GlobalThreadPool::Loop(bound, 0, rangeY); + } else { + auto bound = [&](int a, int b) -> void { + DrawTriangleSlice(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(v0, v1, v2, minX, minY, maxX, maxY, a, b); - }; - GlobalThreadPool::Loop(bound, 0, range); + if (gstate.isModeClear()) { + DrawTriangleSlice(v0, v1, v2, minX, minY, maxX, maxY, true, 0, rangeY); } else { - DrawTriangleSlice(v0, v1, v2, minX, minY, maxX, maxY, 0, range); + DrawTriangleSlice(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. diff --git a/GPU/Software/Rasterizer.h b/GPU/Software/Rasterizer.h index ab9c314c91..53d44e8af0 100644 --- a/GPU/Software/Rasterizer.h +++ b/GPU/Software/Rasterizer.h @@ -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); diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 0b7a40012f..67f929cdf0 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -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); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index a6022747fb..de25539f92 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -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! } diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 48641131dc..8c0bcff4c4 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -33,6 +33,7 @@ #include #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; }; }; diff --git a/GPU/Vulkan/FramebufferVulkan.cpp b/GPU/Vulkan/FramebufferVulkan.cpp index 9cb3019553..1684e70638 100644 --- a/GPU/Vulkan/FramebufferVulkan.cpp +++ b/GPU/Vulkan/FramebufferVulkan.cpp @@ -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; +} \ No newline at end of file diff --git a/GPU/Vulkan/FramebufferVulkan.h b/GPU/Vulkan/FramebufferVulkan.h index 98ac4d9e25..49538dfa99 100644 --- a/GPU/Vulkan/FramebufferVulkan.h +++ b/GPU/Vulkan/FramebufferVulkan.h @@ -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_; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 5a60a9bcbd..b09f060cf6 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -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; diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 277d7ffe0f..940254e7d1 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -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); + } + }); +} \ No newline at end of file diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 15e29864f1..9387ed43fe 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -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 pipelines_; VkPipelineCache pipelineCache_; VulkanContext *vulkan_; + float lineWidth_ = 1.0f; }; diff --git a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp index 45c3b8e994..8b5e746187 100644 --- a/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp +++ b/GPU/Vulkan/VertexShaderGeneratorVulkan.cpp @@ -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) diff --git a/GPU/Vulkan/VulkanUtil.cpp b/GPU/Vulkan/VulkanUtil.cpp index 662f5196c1..fa41eb4bfc 100644 --- a/GPU/Vulkan/VulkanUtil.cpp +++ b/GPU/Vulkan/VulkanUtil.cpp @@ -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; diff --git a/GPU/Vulkan/VulkanUtil.h b/GPU/Vulkan/VulkanUtil.h index 6a6f526236..32b0421e4b 100644 --- a/GPU/Vulkan/VulkanUtil.h +++ b/GPU/Vulkan/VulkanUtil.h @@ -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); } }; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index f4f1a102f0..e85c92e221 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -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(); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index f0f3c0ab12..252d8cffae 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -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 \ diff --git a/ext/native/base/logging.h b/ext/native/base/logging.h index 3bbff36204..4cecf3f8f5 100644 --- a/ext/native/base/logging.h +++ b/ext/native/base/logging.h @@ -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 diff --git a/ext/native/thin3d/VulkanQueueRunner.cpp b/ext/native/thin3d/VulkanQueueRunner.cpp index b2d94a6912..a2e56e483d 100644 --- a/ext/native/thin3d/VulkanQueueRunner.cpp +++ b/ext/native/thin3d/VulkanQueueRunner.cpp @@ -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 }; diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h index 7b70a14f23..c49bb207ff 100644 --- a/ext/native/thin3d/VulkanQueueRunner.h +++ b/ext/native/thin3d/VulkanQueueRunner.h @@ -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; }; diff --git a/ext/native/thin3d/VulkanRenderManager.h b/ext/native/thin3d/VulkanRenderManager.h index 1c17b42ac4..818a44f41b 100644 --- a/ext/native/thin3d/VulkanRenderManager.h +++ b/ext/native/thin3d/VulkanRenderManager.h @@ -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; diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 929e84581a..5b95a0e402 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -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_;