From e027407900dcdaff2adbe951c16c3ce7e2fef67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 9 Jan 2026 20:35:44 +0100 Subject: [PATCH] First reasonable output appears. --- GPU/Common/GPUStateUtils.cpp | 176 +++---------------------- GPU/Common/ShaderCommon.h | 9 +- GPU/Common/ShaderUniforms.cpp | 51 +++---- GPU/Common/ShaderUniforms.h | 18 ++- GPU/Common/SoftwareTransformCommon.cpp | 10 +- GPU/Common/VertexShaderGenerator.cpp | 101 +++++++------- GPU/GLES/ShaderManagerGLES.cpp | 2 + GPU/GLES/ShaderManagerGLES.h | 2 + GPU/GPUCommonHW.cpp | 22 ++-- 9 files changed, 136 insertions(+), 255 deletions(-) diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index ded59dac51..5a3081ab1d 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -637,165 +637,27 @@ void ConvertViewportAndScissor(const DisplayLayoutConfig &config, bool useBuffer DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags()); - if (out.throughMode) { - // If renderX/renderY are offset to compensate for a split framebuffer, - // applying the offset to the viewport isn't enough, since the viewport clips. - // We need to apply either directly to the vertices, or to the "through" projection matrix. - out.viewportX = displayOffsetX; - out.viewportY = displayOffsetY; - out.viewportW = curRTWidth * renderWidthFactor; - out.viewportH = curRTHeight * renderHeightFactor; - out.depthRangeMin = depthScale.EncodeFromU16(0.0f); - out.depthRangeMax = depthScale.EncodeFromU16(65536.0f); - } else { - // These we can turn into a glViewport call, offset by offsetX and offsetY. Math after. - float vpXScale = gstate.getViewportXScale(); - float vpXCenter = gstate.getViewportXCenter(); - float vpYScale = gstate.getViewportYScale(); - float vpYCenter = gstate.getViewportYCenter(); + // If renderX/renderY are offset to compensate for a split framebuffer, + // applying the offset to the viewport isn't enough, since the viewport clips. + // We need to apply either directly to the vertices, or to the "through" projection matrix. + out.viewportX = displayOffsetX; + out.viewportY = displayOffsetY; + out.viewportW = curRTWidth * renderWidthFactor; + out.viewportH = curRTHeight * renderHeightFactor; + // TODO: This clip the entire draw if minz > maxz. + float minz = gstate.getDepthRangeMin(); + float maxz = gstate.getDepthRangeMax(); - // The viewport transform appears to go like this: - // Xscreen = -offsetX + vpXCenter + vpXScale * Xview - // Yscreen = -offsetY + vpYCenter + vpYScale * Yview - // Zscreen = vpZCenter + vpZScale * Zview + out.depthRangeMin = depthScale.EncodeFromU16(minz); + out.depthRangeMax = depthScale.EncodeFromU16(maxz); - // The viewport is normally centered at 2048,2048 but can also be centered at other locations. - // Offset is subtracted from the viewport center and is also set to values in those ranges, and is set so that the viewport will cover - // the desired screen area ([0-480)x[0-272)), so 1808,1912. - - // This means that to get the analogue glViewport we must: - float vpX0 = vpXCenter - offsetX - fabsf(vpXScale); - float vpY0 = vpYCenter - offsetY - fabsf(vpYScale); - gstate_c.vpWidth = vpXScale * 2.0f; - gstate_c.vpHeight = vpYScale * 2.0f; - - float vpWidth = fabsf(gstate_c.vpWidth); - float vpHeight = fabsf(gstate_c.vpHeight); - - float left = renderX + vpX0; - float top = renderY + vpY0; - float right = left + vpWidth; - float bottom = top + vpHeight; - - out.widthScale = 1.0f; - out.xOffset = 0.0f; - out.heightScale = 1.0f; - out.yOffset = 0.0f; - - // If we're within the bounds, we want clipping the viewport way. So leave it be. - { - float overageLeft = std::max(-left, 0.0f); - float overageRight = std::max(right - bufferWidth, 0.0f); - - // Expand viewport to cover scissor region. The viewport doesn't clip on the PSP. - if (right < scissorX2) { - overageRight -= scissorX2 - right; - } - if (left > scissorX1) { - overageLeft += scissorX1 - left; - } - - // Our center drifted by the difference in overages. - float drift = overageRight - overageLeft; - - if (overageLeft != 0.0f || overageRight != 0.0f) { - left += overageLeft; - right -= overageRight; - - // Protect against the viewport being entirely outside the scissor. - // Emit a tiny but valid viewport. Really, we should probably emit a flag to ignore draws. - if (right <= left) { - right = left + 1.0f; - } - - out.widthScale = vpWidth / (right - left); - out.xOffset = drift / (right - left); - } - } - - { - float overageTop = std::max(-top, 0.0f); - float overageBottom = std::max(bottom - bufferHeight, 0.0f); - - // Expand viewport to cover scissor region. The viewport doesn't clip on the PSP. - if (bottom < scissorY2) { - overageBottom -= scissorY2 - bottom; - } - if (top > scissorY1) { - overageTop += scissorY1 - top; - } - // Our center drifted by the difference in overages. - float drift = overageBottom - overageTop; - - if (overageTop != 0.0f || overageBottom != 0.0f) { - top += overageTop; - bottom -= overageBottom; - - // Protect against the viewport being entirely outside the scissor. - // Emit a tiny but valid viewport. Really, we should probably emit a flag to ignore draws. - if (bottom <= top) { - bottom = top + 1.0f; - } - - out.heightScale = vpHeight / (bottom - top); - out.yOffset = drift / (bottom - top); - } - } - - out.viewportX = left * renderWidthFactor + displayOffsetX; - out.viewportY = top * renderHeightFactor + displayOffsetY; - out.viewportW = (right - left) * renderWidthFactor; - out.viewportH = (bottom - top) * renderHeightFactor; - - // The depth viewport parameters are the same, but we handle it a bit differently. - // When clipping is enabled, depth is clamped to [0, 65535]. And minz/maxz discard. - // So, we apply the depth range as minz/maxz, and transform for the viewport. - float vpZScale = gstate.getViewportZScale(); - float vpZCenter = gstate.getViewportZCenter(); - // TODO: This clip the entire draw if minz > maxz. - float minz = gstate.getDepthRangeMin(); - float maxz = gstate.getDepthRangeMax(); - - if (gstate.isDepthClampEnabled() && (minz == 0 || maxz == 65535)) { - // Here, we should "clamp." But clamping per fragment would be slow. - // So, instead, we just increase the available range and hope. - // If depthSliceFactor is 4, it means (75% / 2) of the depth lies in each direction. - float fullDepthRange = 65535.0f * (depthScale.Scale() - 1.0f) * (1.0f / 2.0f); - if (minz == 0) { - minz -= fullDepthRange; - } - if (maxz == 65535) { - maxz += fullDepthRange; - } - } else if (maxz == 65535) { - // This means clamp isn't enabled, but we still want to allow values up to 65535.99. - // If DepthSliceFactor() is 1.0, though, this would make out.depthRangeMax exceed 1. - // Since that would clamp, it would make Z=1234 not match between draws when maxz changes. - if (depthScale.Scale() > 1.0f) - maxz = 65535.99f; - } - - // Okay. So, in our shader, -1 will map to minz, and +1 will map to maxz. - float halfActualZRange = (maxz - minz) * (1.0f / 2.0f); - out.depthScale = halfActualZRange < std::numeric_limits::epsilon() ? 1.0f : vpZScale / halfActualZRange; - // This adjusts the center from halfActualZRange to vpZCenter. - out.zOffset = halfActualZRange < std::numeric_limits::epsilon() ? 0.0f : (vpZCenter - (minz + halfActualZRange)) / halfActualZRange; - - if (!gstate_c.Use(GPU_USE_ACCURATE_DEPTH)) { - out.depthScale = 1.0f; - out.zOffset = 0.0f; - out.depthRangeMin = depthScale.EncodeFromU16(vpZCenter - vpZScale); - out.depthRangeMax = depthScale.EncodeFromU16(vpZCenter + vpZScale); - } else { - out.depthRangeMin = depthScale.EncodeFromU16(minz); - out.depthRangeMax = depthScale.EncodeFromU16(maxz); - } - - // OpenGL will clamp these for us anyway, and Direct3D will error if not clamped. - // Of course, if this happens we've skewed out.depthScale/out.zOffset and may get z-fighting. - out.depthRangeMin = std::max(out.depthRangeMin, 0.0f); - out.depthRangeMax = std::min(out.depthRangeMax, 1.0f); - } + // Not really using these now. + out.widthScale = 1.0f; + out.heightScale = 1.0f; + out.depthScale = 1.0f; + out.zOffset = 0.0f; + out.xOffset = 0.0f; + out.yOffset = 0.0f; } void UpdateCachedViewportState(const ViewportAndScissor &vpAndScissor) { diff --git a/GPU/Common/ShaderCommon.h b/GPU/Common/ShaderCommon.h index 95cf3908f2..141b28eabb 100644 --- a/GPU/Common/ShaderCommon.h +++ b/GPU/Common/ShaderCommon.h @@ -70,6 +70,9 @@ enum : uint64_t { DIRTY_UVSCALEOFFSET = 1ULL << 18, DIRTY_DEPTHRANGE = 1ULL << 19, + DIRTY_VIEWPORT_UNIFORMS = 1ULL << 20, + // Free bit 20! + DIRTY_WORLDMATRIX = 1ULL << 21, DIRTY_VIEWMATRIX = 1ULL << 22, DIRTY_TEXMATRIX = 1ULL << 23, @@ -93,12 +96,14 @@ enum : uint64_t { DIRTY_LIGHT_CONTROL = 1ULL << 38, DIRTY_TEX_ALPHA_MUL = 1ULL << 39, - // Bits 40-42 are free for new uniforms. Then we're really out and need to start merging. + DIRTY_RASTER_OFFSET = 1ULL << 40, + + // Bits 41-42 are free for new uniforms (although the mask below needs updating). Then we're really out and need to start merging. // Don't forget to update DIRTY_ALL_UNIFORMS when you start using them. DIRTY_BONE_UNIFORMS = 0xFF000000ULL, - DIRTY_ALL_UNIFORMS = 0x0FFFFFFFFFFULL, + DIRTY_ALL_UNIFORMS = 0x1FFFFFFFFFFULL, // Other dirty elements that aren't uniforms DIRTY_CULL_PLANES = 1ULL << 43, diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index 54052f7ebe..7df0f75f24 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -14,18 +14,6 @@ using namespace Lin; -static void ConvertProjMatrixToVulkan(Matrix4x4 &in) { - const Vec3 trans(gstate_c.vpXOffset, gstate_c.vpYOffset, gstate_c.vpZOffset * 0.5f + 0.5f); - const Vec3 scale(gstate_c.vpWidthScale, gstate_c.vpHeightScale, gstate_c.vpDepthScale * 0.5f); - in.translateAndScale(trans, scale); -} - -static void ConvertProjMatrixToD3D11(Matrix4x4 &in) { - const Vec3 trans(gstate_c.vpXOffset, -gstate_c.vpYOffset, gstate_c.vpZOffset * 0.5f + 0.5f); - const Vec3 scale(gstate_c.vpWidthScale, -gstate_c.vpHeightScale, gstate_c.vpDepthScale * 0.5f); - in.translateAndScale(trans, scale); -} - void CalcCullRange(float minValues[4], float maxValues[4], bool flipViewport, bool hasNegZ) { // Account for the projection viewport adjustment when viewport is too large. auto reverseViewportX = [](float x) { @@ -115,28 +103,12 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView Matrix4x4 flippedMatrix; memcpy(&flippedMatrix, gstate.projMatrix, 16 * sizeof(float)); - const bool invertedY = gstate_c.vpHeight < 0; - if (invertedY) { - flippedMatrix[1] = -flippedMatrix[1]; - flippedMatrix[5] = -flippedMatrix[5]; - flippedMatrix[9] = -flippedMatrix[9]; - flippedMatrix[13] = -flippedMatrix[13]; - } - const bool invertedX = gstate_c.vpWidth < 0; - if (invertedX) { - flippedMatrix[0] = -flippedMatrix[0]; - flippedMatrix[4] = -flippedMatrix[4]; - flippedMatrix[8] = -flippedMatrix[8]; - flippedMatrix[12] = -flippedMatrix[12]; - } - if (flipViewport) { - ConvertProjMatrixToD3D11(flippedMatrix); - } else { - ConvertProjMatrixToVulkan(flippedMatrix); - } + // TODO: Flip something for D3D11. if (!useBufferedRendering && g_display.rotation != DisplayRotation::ROTATE_0) { - flippedMatrix = flippedMatrix * g_display.rot_matrix; + // TODO: We have to apply the rotation some other way now that we've moved the viewport + // into the vertex shader. + // flippedMatrix = flippedMatrix * g_display.rot_matrix; } CopyMatrix4x4(ub->proj, flippedMatrix.getReadPtr()); @@ -153,6 +125,21 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView CopyFloat4(ub->xywh, xywh); } + if (dirtyUniforms & DIRTY_RASTER_OFFSET) { + ub->rasterOffset[0] = gstate.getOffsetX(); + ub->rasterOffset[1] = gstate.getOffsetY(); + } + + if (dirtyUniforms & DIRTY_VIEWPORT_UNIFORMS) { + // TODO: This should be a couple of SIMD instructions. + ub->vpScale[0] = gstate.getViewportXScale(); + ub->vpScale[1] = gstate.getViewportYScale(); + ub->vpScale[2] = gstate.getViewportZScale(); + ub->vpOffset[0] = gstate.getViewportXCenter(); + ub->vpOffset[1] = gstate.getViewportYCenter(); + ub->vpOffset[2] = gstate.getViewportZCenter(); + } + // Transform if (dirtyUniforms & DIRTY_WORLDMATRIX) { ConvertMatrix4x3To3x4Transposed(ub->world, gstate.worldMatrix); diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index 17bb383527..bcb125150f 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -11,20 +11,23 @@ enum : uint64_t { DIRTY_WORLDMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWMATRIX | DIRTY_TEXMATRIX | DIRTY_ALPHACOLORREF | DIRTY_PROJMATRIX | DIRTY_FOGCOLOR | DIRTY_FOGCOEF | DIRTY_TEXENV | DIRTY_TEX_ALPHA_MUL | DIRTY_STENCILREPLACEVALUE | DIRTY_ALPHACOLORMASK | DIRTY_SHADERBLEND | DIRTY_COLORWRITEMASK | DIRTY_UVSCALEOFFSET | DIRTY_TEXCLAMP | DIRTY_DEPTHRANGE | DIRTY_MATAMBIENTALPHA | - DIRTY_BEZIERSPLINE | DIRTY_DEPAL, + DIRTY_BEZIERSPLINE | DIRTY_DEPAL | DIRTY_VIEWPORT_UNIFORMS | DIRTY_RASTER_OFFSET, DIRTY_LIGHT_UNIFORMS = DIRTY_LIGHT_CONTROL | DIRTY_LIGHT0 | DIRTY_LIGHT1 | DIRTY_LIGHT2 | DIRTY_LIGHT3 | DIRTY_MATDIFFUSE | DIRTY_MATSPECULAR | DIRTY_MATEMISSIVE | DIRTY_AMBIENT, }; -// Currently 496 bytes. +// Currently 480 bytes. // Every line here is a 4-float. struct alignas(16) UB_VS_FS_Base { float proj[16]; - float xywh[4]; // replacement for the old proj_through float view[12]; float world[12]; float tex[12]; + float xywh[4]; // replacement for the old proj_through + float vpScale[3]; float padding1; + float vpOffset[3]; float padding2; + float rasterOffset[2]; float padding3[2]; float uvScaleOffset[4]; float depthRange[4]; float matAmbient[4]; @@ -33,7 +36,7 @@ struct alignas(16) UB_VS_FS_Base { uint32_t spline_counts; uint32_t depal_mask_shift_off_fmt; // 4 params packed into one. uint32_t colorWriteMask; float mipBias; // Fragment data - float texNoAlpha; float texMul; float padding[2]; // this vec4 will hold ubershader stuff. We won't use integer flags in the fragment shader. + float texNoAlpha; float texMul; float padding4[2]; // this vec4 will hold ubershader stuff. We won't use integer flags in the fragment shader. float fogColor[3]; uint32_t alphaColorRef; float texEnvColor[3]; uint32_t colorTestMask; float texClamp[4]; @@ -44,12 +47,17 @@ struct alignas(16) UB_VS_FS_Base { // with just uploading the first 448 bytes of the struct (up to and including fogCoef). }; +static_assert(sizeof(UB_VS_FS_Base) == 480); + static const char * const ub_baseStr = R"( mat4 u_proj; - vec4 u_xywh; mat3x4 u_view; mat3x4 u_world; mat3x4 u_texmtx; + vec4 u_xywh; + vec4 u_vpScale; // w = offsetX + vec4 u_vpOffset; // w = offsetY + vec2 u_rasterOffset; vec2 pad0; vec4 u_uvscaleoffset; vec4 u_depthRange; vec4 u_matambientalpha; diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 262779f6d9..848a183fdc 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -567,15 +567,15 @@ void SoftwareTransform::BuildDrawingParams(int prim, int vertexCount, u32 vertTy void SoftwareTransform::CalcCullParams(float &minZValue, float &maxZValue) const { // The projected Z can be up to 0x3F8000FF, which is where this constant is from. // It seems like it may only maintain 15 mantissa bits (excluding implied.) - maxZValue = 1.000030517578125f * gstate_c.vpDepthScale; + maxZValue = 1.000030517578125f * gstate.getViewportZScale(); minZValue = -maxZValue; // Scale and offset the Z appropriately, since we baked that into a projection transform. if (params_.usesHalfZ) { - maxZValue = maxZValue * 0.5f + 0.5f + gstate_c.vpZOffset * 0.5f; - minZValue = minZValue * 0.5f + 0.5f + gstate_c.vpZOffset * 0.5f; + maxZValue = maxZValue * 0.5f + 0.5f + gstate.getViewportZCenter() * 0.5f; + minZValue = minZValue * 0.5f + 0.5f + gstate.getViewportZCenter() * 0.5f; } else { - maxZValue += gstate_c.vpZOffset; - minZValue += gstate_c.vpZOffset; + maxZValue += gstate.getViewportZCenter(); + minZValue += gstate.getViewportZCenter(); } // In case scale was negative, flip. if (minZValue > maxZValue) diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index dcbc69e447..95356f7555 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -747,11 +747,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " %sv_fogdepth = fog;\n", compat.vsOutPrefix); if (isModeThrough) { // The proj_through matrix already has the rotation, if needed. - WRITE(p, " vec4 outPos;\n"); - WRITE(p, " outPos.x = ((position.x - u_xywh.x) / u_xywh.z) * 2.0 - 1.0;"); - WRITE(p, " outPos.y = ((position.y - u_xywh.y) / u_xywh.w) * 2.0 - 1.0;"); - WRITE(p, " outPos.z = position.z;\n"); - WRITE(p, " outPos.w = 1.0;\n"); + WRITE(p, " vec4 outPos = vec4(position.x, position.y, position.z, 1.0);\n"); } else { if (compat.shaderLanguage == GLSL_VULKAN) { // Apply rotation from the uniform. @@ -772,6 +768,10 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag } else { WRITE(p, " vec4 outPos = pos;\n"); } + + // Here we need to apply the viewport and also range culling, just like with the hardware transform below. + + } } else { // Step 1: World Transform / Skinning @@ -866,6 +866,54 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag } } + // Perform the perspective projection and viewport transform. + WRITE(p, " outPos.xyz = (outPos.xyz / outPos.w) * u_vpScale.xyz + u_vpOffset.xyz;\n"); + // We leave W as is to preserve the perspective correct interpolation. + + if (vertexRangeCulling) { + // Position is now already projected here. + WRITE(p, " vec3 projPos = outPos.xyz;\n"); + WRITE(p, " float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n"); + + if (!bugs.Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) { + // Vertex range culling doesn't happen when Z clips, note sign of w is important. + WRITE(p, " if (u_cullRangeMin.w <= 0.0 || projZ * outPos.w > -outPos.w) {\n"); + const char *outMin = "projPos.x < 0.0 || projPos.y < 0.0"; + const char *outMax = "projPos.x > 4096.0 || projPos.y > 4096.0"; + WRITE(p, " if ((%s) || (%s)) {\n", outMin, outMax); + WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n"); + WRITE(p, " }\n"); + WRITE(p, " }\n"); + WRITE(p, " if (u_cullRangeMin.w <= 0.0) {\n"); + WRITE(p, " if (projPos.z < u_cullRangeMin.z || projPos.z > u_cullRangeMax.z) {\n"); + WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n"); + WRITE(p, " }\n"); + WRITE(p, " }\n"); + } + + const char *cull0 = compat.shaderLanguage == HLSL_D3D11 ? ".x" : "[0]"; + const char *cull1 = compat.shaderLanguage == HLSL_D3D11 ? ".y" : "[1]"; + if (gstate_c.Use(GPU_USE_CLIP_DISTANCE)) { + // TODO: Ignore triangles from GE_PRIM_RECTANGLES in transform mode, which should not clip to neg z. + // We add a small amount to prevent errors as in #15816 (PSP Z is only 16-bit fixed point, anyway.) + WRITE(p, " %sgl_ClipDistance%s = projZ * outPos.w + outPos.w + %f;\n", compat.vsOutPrefix, vertexRangeClipSuffix, 0.0625 / 65536.0); + } + if (gstate_c.Use(GPU_USE_CULL_DISTANCE)) { + // Cull any triangle fully outside in the same direction when depth clamp enabled. + // We check u_depthRange in case depthScale was zero - in that case we can't work out the cull distance. + WRITE(p, " if (u_cullRangeMin.w > 0.0 && u_depthRange.w != 0.0f) {\n"); + WRITE(p, " %sgl_CullDistance%s = projPos.z - u_cullRangeMin.z;\n", compat.vsOutPrefix, cull0); + WRITE(p, " %sgl_CullDistance%s = u_cullRangeMax.z - projPos.z;\n", compat.vsOutPrefix, cull1); + WRITE(p, " } else {\n"); + WRITE(p, " %sgl_CullDistance%s = 0.0;\n", compat.vsOutPrefix, cull0); + WRITE(p, " %sgl_CullDistance%s = 0.0;\n", compat.vsOutPrefix, cull1); + WRITE(p, " }\n"); + } + } + + // Apply raster offset after the range culling. + WRITE(p, " outPos.xy -= u_rasterOffset.xy;\n"); + // TODO: Declare variables for dots for shade mapping if needed. const char *srcCol = "color0"; @@ -1230,45 +1278,10 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag WRITE(p, " }\n"); } - if (vertexRangeCulling) { - WRITE(p, " vec3 projPos = outPos.xyz / outPos.w;\n"); - WRITE(p, " float projZ = (projPos.z - u_depthRange.z) * u_depthRange.w;\n"); - - if (!bugs.Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) { - // Vertex range culling doesn't happen when Z clips, note sign of w is important. - WRITE(p, " if (u_cullRangeMin.w <= 0.0 || projZ * outPos.w > -outPos.w) {\n"); - const char *outMin = "projPos.x < u_cullRangeMin.x || projPos.y < u_cullRangeMin.y"; - const char *outMax = "projPos.x > u_cullRangeMax.x || projPos.y > u_cullRangeMax.y"; - WRITE(p, " if ((%s) || (%s)) {\n", outMin, outMax); - WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n"); - WRITE(p, " }\n"); - WRITE(p, " }\n"); - WRITE(p, " if (u_cullRangeMin.w <= 0.0) {\n"); - WRITE(p, " if (projPos.z < u_cullRangeMin.z || projPos.z > u_cullRangeMax.z) {\n"); - WRITE(p, " outPos.xyzw = u_cullRangeMax.wwww;\n"); - WRITE(p, " }\n"); - WRITE(p, " }\n"); - } - - const char *cull0 = compat.shaderLanguage == HLSL_D3D11 ? ".x" : "[0]"; - const char *cull1 = compat.shaderLanguage == HLSL_D3D11 ? ".y" : "[1]"; - if (gstate_c.Use(GPU_USE_CLIP_DISTANCE)) { - // TODO: Ignore triangles from GE_PRIM_RECTANGLES in transform mode, which should not clip to neg z. - // We add a small amount to prevent error as in #15816 (PSP Z is only 16-bit fixed point, anyway.) - WRITE(p, " %sgl_ClipDistance%s = projZ * outPos.w + outPos.w + %f;\n", compat.vsOutPrefix, vertexRangeClipSuffix, 0.0625 / 65536.0); - } - if (gstate_c.Use(GPU_USE_CULL_DISTANCE)) { - // Cull any triangle fully outside in the same direction when depth clamp enabled. - // We check u_depthRange in case depthScale was zero - in that case we can't work out the cull distance. - WRITE(p, " if (u_cullRangeMin.w > 0.0 && u_depthRange.w != 0.0f) {\n"); - WRITE(p, " %sgl_CullDistance%s = projPos.z - u_cullRangeMin.z;\n", compat.vsOutPrefix, cull0); - WRITE(p, " %sgl_CullDistance%s = u_cullRangeMax.z - projPos.z;\n", compat.vsOutPrefix, cull1); - WRITE(p, " } else {\n"); - WRITE(p, " %sgl_CullDistance%s = 0.0;\n", compat.vsOutPrefix, cull0); - WRITE(p, " %sgl_CullDistance%s = 0.0;\n", compat.vsOutPrefix, cull1); - WRITE(p, " }\n"); - } - } + // Convert to NDC space. + WRITE(p, " outPos.x = ((outPos.x - u_xywh.x) / u_xywh.z) * 2.0 - 1.0;\n"); + WRITE(p, " outPos.y = ((outPos.y - u_xywh.y) / u_xywh.w) * 2.0 - 1.0;\n"); + WRITE(p, " outPos.z = outPos.z / 65535.0;\n"); // We've named the output gl_Position in HLSL as well. WRITE(p, " %sgl_Position = outPos;\n", compat.vsOutPrefix); diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index 2da247114d..65177705fb 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -106,6 +106,8 @@ LinkedShader::LinkedShader(GLRenderManager *render, VShaderID VSID, Shader *vs, queries.push_back({ &u_proj, "u_proj" }); queries.push_back({ &u_proj_lens, "u_proj_lens" }); queries.push_back({ &u_xywh, "u_xywh" }); + queries.push_back({ &u_vpScale, "u_vpScale" }); + queries.push_back({ &u_vpOffset, "u_vpOffset" }); queries.push_back({ &u_texenv, "u_texenv" }); queries.push_back({ &u_fogcolor, "u_fogcolor" }); queries.push_back({ &u_fogcoef, "u_fogcoef" }); diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index e069cb473e..046b64966c 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -61,6 +61,8 @@ public: int u_proj; int u_proj_lens; int u_xywh; + int u_vpScale; + int u_vpOffset; int u_texenv; int u_view; int u_texmtx; diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index b07273fc59..b99369c4eb 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -191,19 +191,19 @@ const CommonCommandTableEntry commonCommandTable[] = { { GE_CMD_ANTIALIASENABLE, FLAG_FLUSHBEFOREONCHANGE }, // Viewport. - { GE_CMD_OFFSETX, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, - { GE_CMD_OFFSETY, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, - { GE_CMD_VIEWPORTXSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES }, - { GE_CMD_VIEWPORTYSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES }, - { GE_CMD_VIEWPORTXCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES }, - { GE_CMD_VIEWPORTYCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES }, - { GE_CMD_VIEWPORTZSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, - { GE_CMD_VIEWPORTZCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE }, + { GE_CMD_OFFSETX, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES | DIRTY_RASTER_OFFSET }, + { GE_CMD_OFFSETY, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES | DIRTY_RASTER_OFFSET }, + { GE_CMD_VIEWPORTXSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES | DIRTY_VIEWPORT_UNIFORMS }, + { GE_CMD_VIEWPORTYSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES | DIRTY_VIEWPORT_UNIFORMS }, + { GE_CMD_VIEWPORTXCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES | DIRTY_VIEWPORT_UNIFORMS }, + { GE_CMD_VIEWPORTYCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULL_PLANES | DIRTY_VIEWPORT_UNIFORMS }, + { GE_CMD_VIEWPORTZSCALE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VIEWPORT_UNIFORMS }, + { GE_CMD_VIEWPORTZCENTER, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_CULLRANGE | DIRTY_PROJMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VIEWPORT_UNIFORMS }, { GE_CMD_DEPTHCLAMPENABLE, FLAG_FLUSHBEFOREONCHANGE, DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_RASTER_STATE }, // Z clip - { GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, - { GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE }, + { GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE }, + { GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_DEPTHRANGE }, // Region { GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_FRAMEBUF | DIRTY_TEXTURE_PARAMS | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE | DIRTY_CULL_PLANES }, @@ -592,6 +592,7 @@ u32 GPUCommonHW::CheckGPUFeatures() const { features |= GPU_USE_BLEND_MINMAX; } + /* if (draw_->GetDeviceCaps().clipDistanceSupported) { features |= GPU_USE_CLIP_DISTANCE; } @@ -599,6 +600,7 @@ u32 GPUCommonHW::CheckGPUFeatures() const { if (draw_->GetDeviceCaps().cullDistanceSupported) { features |= GPU_USE_CULL_DISTANCE; } + */ if (draw_->GetDeviceCaps().textureDepthSupported) { features |= GPU_USE_DEPTH_TEXTURE;