From a0907ab7bb41f62a263727f952e45f36fa37562f Mon Sep 17 00:00:00 2001 From: xebra Date: Thu, 2 Mar 2017 21:32:01 +0900 Subject: [PATCH 1/2] [spline/bezier]Fix shared uniform a bit. --- GPU/Common/ShaderUniforms.cpp | 2 +- GPU/Vulkan/GPU_Vulkan.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index ac360e7a6b..9c76b106db 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -189,7 +189,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView if (gstate_c.bezier) { if (dirtyUniforms & DIRTY_BEZIERCOUNTU) - ub->spline_count_u = gstate_c.bezier_count_u; + ub->spline_count_u = gstate_c.spline_count_u; } else if (gstate_c.spline) { if (dirtyUniforms & DIRTY_SPLINECOUNTU) ub->spline_count_u = gstate_c.spline_count_u; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 98bc877935..9f08dfbbb4 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -879,9 +879,9 @@ void GPU_Vulkan::Execute_Bezier(u32 op, u32 diff) { if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { gstate_c.bezier = true; - if (gstate_c.bezier_count_u != bz_ucount) { + if (gstate_c.spline_count_u != bz_ucount) { gstate_c.Dirty(DIRTY_BEZIERCOUNTU); - gstate_c.bezier_count_u = bz_ucount; + gstate_c.spline_count_u = bz_ucount; } } From 7364dbea3bc912d68620f644177561eea6089402 Mon Sep 17 00:00:00 2001 From: xebra Date: Sat, 4 Mar 2017 22:46:55 +0900 Subject: [PATCH 2/2] [spline/bezier]Implement hardware tessellation on D3D11. --- GPU/Common/ShaderUniforms.h | 8 +- GPU/D3D11/DrawEngineD3D11.cpp | 81 +++++++- GPU/D3D11/DrawEngineD3D11.h | 21 +- GPU/D3D11/GPU_D3D11.cpp | 45 ++++- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 225 +++++++++++++++++++++- UI/GameSettingsScreen.cpp | 23 ++- 6 files changed, 368 insertions(+), 35 deletions(-) diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index fe30da1a63..3189c31d12 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -80,10 +80,10 @@ R"( float4x4 u_proj; float4 u_depthRange; float3 u_fogcoef_stencilreplace; float4 u_matambientalpha; - int spline_count_u; - int spline_count_v; - int spline_type_u; - int spline_type_v; + int u_spline_count_u; + int u_spline_count_v; + int u_spline_type_u; + int u_spline_type_v; float3 u_fogcolor; float3 u_texenv; uint4 u_alphacolorref; diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index e822408af8..ca6e74e17d 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -123,7 +123,7 @@ void DrawEngineD3D11::InitDeviceObjects() { pushVerts_ = new PushBufferD3D11(device_, VERTEX_PUSH_SIZE, D3D11_BIND_VERTEX_BUFFER); pushInds_ = new PushBufferD3D11(device_, INDEX_PUSH_SIZE, D3D11_BIND_INDEX_BUFFER); - tessDataTransfer = new TessellationDataTransferD3D11(); + tessDataTransfer = new TessellationDataTransferD3D11(context_, device_); } void DrawEngineD3D11::ClearTrackedVertexArrays() { @@ -796,7 +796,10 @@ rotateVBO: memcpy(iptr, decIndex, iSize); pushInds_->EndPush(context_); context_->IASetIndexBuffer(pushInds_->Buf(), DXGI_FORMAT_R16_UINT, iOffset); - context_->DrawIndexed(vertexCount, 0, 0); + if (gstate_c.bezier || gstate_c.spline) + context_->DrawIndexedInstanced(vertexCount, numPatches, 0, 0, 0); + else + context_->DrawIndexed(vertexCount, 0, 0); } else { context_->Draw(vertexCount, 0); } @@ -805,7 +808,10 @@ rotateVBO: context_->IASetVertexBuffers(0, 1, &vb_, &stride, &offset); if (useElements) { context_->IASetIndexBuffer(ib_, DXGI_FORMAT_R16_UINT, 0); - context_->DrawIndexed(vertexCount, 0, 0); + if (gstate_c.bezier || gstate_c.spline) + context_->DrawIndexedInstanced(vertexCount, numPatches, 0, 0, 0); + else + context_->DrawIndexed(vertexCount, 0, 0); } else { context_->Draw(vertexCount, 0); } @@ -961,7 +967,70 @@ bool DrawEngineD3D11::IsCodePtrVertexDecoder(const u8 *ptr) const { return decJitCache_->IsInSpace(ptr); } -void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) -{ - // HW tessellation not yet supported +void DrawEngineD3D11::TessellationDataTransferD3D11::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) { + // Position + if (prevSize < size) { + prevSize = size; + if (data_tex[0]) { + data_tex[0]->Release(); + view[0]->Release(); + } + desc.Width = size; + desc.Format = DXGI_FORMAT_R32G32B32_FLOAT; + HRESULT hr = device_->CreateTexture1D(&desc, nullptr, &data_tex[0]); + if (FAILED(hr)) { + INFO_LOG(G3D, "Failed to create D3D texture for HW tessellation"); + data_tex[0]->Release(); + return; // TODO: Turn off HW tessellation if texture creation error occured. + } + hr = device_->CreateShaderResourceView(data_tex[0], nullptr, &view[0]); + context_->VSSetShaderResources(0, 1, &view[0]); + } + dstBox.right = size; + context_->UpdateSubresource(data_tex[0], 0, &dstBox, pos, 0, 0); + + // Texcoords + if (hasTexCoords) { + if (prevSizeTex < size) { + prevSizeTex = size; + if (data_tex[1]) { + data_tex[1]->Release(); + view[1]->Release(); + } + desc.Width = size; + desc.Format = DXGI_FORMAT_R32G32B32_FLOAT; + HRESULT hr = device_->CreateTexture1D(&desc, nullptr, &data_tex[1]); + if (FAILED(hr)) { + INFO_LOG(G3D, "Failed to create D3D texture for HW tessellation"); + data_tex[1]->Release(); + return; + } + hr = device_->CreateShaderResourceView(data_tex[1], nullptr, &view[1]); + context_->VSSetShaderResources(1, 1, &view[1]); + } + dstBox.right = size; + context_->UpdateSubresource(data_tex[1], 0, &dstBox, tex, 0, 0); + } + + // Color + int sizeColor = hasColor ? size : 1; + if (prevSizeCol < sizeColor) { + prevSizeCol = sizeColor; + if (data_tex[2]) { + data_tex[2]->Release(); + view[2]->Release(); + } + desc.Width = sizeColor; + desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + HRESULT hr = device_->CreateTexture1D(&desc, nullptr, &data_tex[2]); + if (FAILED(hr)) { + INFO_LOG(G3D, "Failed to create D3D texture for HW tessellation"); + data_tex[2]->Release(); + return; + } + hr = device_->CreateShaderResourceView(data_tex[2], nullptr, &view[2]); + context_->VSSetShaderResources(2, 1, &view[2]); + } + dstBox.right = sizeColor; + context_->UpdateSubresource(data_tex[2], 0, &dstBox, col, 0, 0); } diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index 4581e1faeb..61064d7218 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -256,11 +256,28 @@ private: // Hardware tessellation class TessellationDataTransferD3D11 : public TessellationDataTransfer { private: - int data_tex[3]; + ID3D11DeviceContext *context_; + ID3D11Device *device_; + ID3D11Texture1D *data_tex[3]; + ID3D11ShaderResourceView *view[3]; + D3D11_TEXTURE1D_DESC desc; + D3D11_BOX dstBox; public: - TessellationDataTransferD3D11() : TessellationDataTransfer(), data_tex() { + TessellationDataTransferD3D11(ID3D11DeviceContext *context_, ID3D11Device *device_) + : TessellationDataTransfer(), context_(context_), device_(device_), data_tex(), view(), desc(), dstBox{0, 0, 0, 1, 1, 1} { + desc.CPUAccessFlags = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.ArraySize = 1; + desc.MipLevels = 1; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; } ~TessellationDataTransferD3D11() { + for (int i = 0; i < 3; i++) { + if (data_tex[i]) { + data_tex[i]->Release(); + view[i]->Release(); + } + } } void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; }; diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 6073a06eb6..7a83acfeea 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -477,11 +477,13 @@ GPU_D3D11::GPU_D3D11(GraphicsContext *gfxCtx, Draw::DrawContext *draw) textureCache_->NotifyConfigChanged(); if (g_Config.bHardwareTessellation) { - // Disable hardware tessellation bacause DX11 is still unsupported. - g_Config.bHardwareTessellation = false; - ERROR_LOG(G3D, "Hardware Tessellation is unsupported, falling back to software tessellation"); - I18NCategory *gr = GetI18NCategory("Graphics"); - host->NotifyUserMessage(gr->T("Turn off Hardware Tessellation - unsupported"), 2.5f, 0xFF3030FF); + if (false) { // TODO: Check GPU features + // Disable hardware tessellation. + g_Config.bHardwareTessellation = false; + ERROR_LOG(G3D, "Hardware Tessellation is unsupported, falling back to software tessellation"); + I18NCategory *gr = GetI18NCategory("Graphics"); + host->NotifyUserMessage(gr->T("Turn off Hardware Tessellation - unsupported"), 2.5f, 0xFF3030FF); + } } } @@ -874,9 +876,20 @@ void GPU_D3D11::Execute_Bezier(u32 op, u32 diff) { int bz_vcount = (op >> 8) & 0xFF; bool computeNormals = gstate.isLightingEnabled(); bool patchFacing = gstate.patchfacing & 1; + + if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + gstate_c.bezier = true; + if (gstate_c.spline_count_u != bz_ucount) { + gstate_c.Dirty(DIRTY_BEZIERCOUNTU); + gstate_c.spline_count_u = bz_ucount; + } + } + int bytesRead = 0; drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); + gstate_c.bezier = false; + // After drawing, we advance pointers - see SubmitPrim which does the same. int count = bz_ucount * bz_vcount; AdvanceVerts(gstate.vertType, count, bytesRead); @@ -922,9 +935,31 @@ void GPU_D3D11::Execute_Spline(u32 op, u32 diff) { bool computeNormals = gstate.isLightingEnabled(); bool patchFacing = gstate.patchfacing & 1; u32 vertType = gstate.vertType; + + if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + gstate_c.spline = true; + if (gstate_c.spline_count_u != sp_ucount) { + gstate_c.Dirty(DIRTY_SPLINECOUNTU); + gstate_c.spline_count_u = sp_ucount; + } + if (gstate_c.spline_count_v != sp_vcount) { + gstate_c.Dirty(DIRTY_SPLINECOUNTV); + gstate_c.spline_count_v = sp_vcount; + } + if (gstate_c.spline_type_u != sp_utype) { + gstate_c.Dirty(DIRTY_SPLINETYPEU); + gstate_c.spline_type_u = sp_utype; + } + if (gstate_c.spline_type_v != sp_vtype) { + gstate_c.Dirty(DIRTY_SPLINETYPEV); + gstate_c.spline_type_v = sp_vtype; + } + } int bytesRead = 0; drawEngine_.SubmitSpline(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), sp_ucount, sp_vcount, sp_utype, sp_vtype, patchPrim, computeNormals, patchFacing, vertType, &bytesRead); + gstate_c.spline = false; + // After drawing, we advance pointers - see SubmitPrim which does the same. int count = sp_ucount * sp_vcount; AdvanceVerts(gstate.vertType, count, bytesRead); diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 12a5523c91..03bcef0562 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -84,6 +84,12 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE); int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3); + bool doBezier = id.Bit(VS_BIT_BEZIER); + bool doSpline = id.Bit(VS_BIT_SPLINE); + bool hasColorTess = id.Bit(VS_BIT_HAS_COLOR_TESS); + bool hasTexcoordTess = id.Bit(VS_BIT_HAS_TEXCOORD_TESS); + bool flipNormalTess = id.Bit(VS_BIT_NORM_REVERSE_TESS); + bool scaleUV = !throughmode && (uvGenMode == GE_TEXMAP_TEXTURE_COORDS || uvGenMode == GE_TEXMAP_UNKNOWN); DoLightComputation doLight[4] = { LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF }; @@ -185,6 +191,9 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l bool texCoordInVec3 = false; if (useHWTransform) { WRITE(p, "struct VS_IN { \n"); + if ((doSpline || doBezier) && lang == HLSL_D3D11) { + WRITE(p, " uint instanceId : SV_InstanceID;\n"); + } if (enableBones) { WRITE(p, " %s", boneWeightAttrDecl[numBoneWeights]); } @@ -253,6 +262,88 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l WRITE(p, "}\n\n"); } + // Hardware tessellation + if (doSpline || doBezier) { + if (lang == HLSL_D3D11) { + WRITE(p, "Texture1D u_tess_pos_tex : register(t0);\n"); + WRITE(p, "Texture1D u_tess_tex_tex : register(t1);\n"); + WRITE(p, "Texture1D u_tess_col_tex : register(t2);\n"); + } + + const char *init[3] = { "0.0, 0.0", "0.0, 0.0, 0.0", "0.0, 0.0, 0.0, 0.0" }; + for (int i = 2; i <= 4; i++) { + // Define 3 types float2, float3, float4 + WRITE(p, "float%d tess_sample(in float%d points[16], in float2 weights[4]) {\n", i, i); + WRITE(p, " float%d pos = float%d(%s);\n", i, i, init[i - 2]); + WRITE(p, " for (int i = 0; i < 4; ++i) {\n"); + WRITE(p, " for (int j = 0; j < 4; ++j) {\n"); + WRITE(p, " float f = weights[j].x * weights[i].y;\n"); + WRITE(p, " if (f != 0.0)\n"); + WRITE(p, " pos = pos + f * points[i * 4 + j];\n"); + WRITE(p, " }\n"); + WRITE(p, " }\n"); + WRITE(p, " return pos;\n"); + WRITE(p, "}\n"); + } + if (doSpline) { + WRITE(p, "void spline_knot(int2 num_patches, int2 type, out float2 knot[6], int2 patch_pos) {\n"); + WRITE(p, " for (int i = 0; i < 6; ++i) {\n"); + WRITE(p, " knot[i] = float2(i + patch_pos.x - 2, i + patch_pos.y - 2);\n"); + WRITE(p, " }\n"); + // WRITE(p, " if ((type.x & 1) != 0) {\n"); + WRITE(p, " if ((type.x == 1) || (type.x == 3)) {\n"); + WRITE(p, " if (patch_pos.x <= 2)\n"); + WRITE(p, " knot[0].x = 0.0;\n"); + WRITE(p, " if (patch_pos.x <= 1)\n"); + WRITE(p, " knot[1].x = 0.0;\n"); + WRITE(p, " }\n"); + // WRITE(p, " if ((type.x & 2) != 0) {\n"); + WRITE(p, " if ((type.x == 2) || (type.x == 3)) {\n"); + WRITE(p, " if (patch_pos.x >= (num_patches.x - 2))\n"); + WRITE(p, " knot[5].x = num_patches.x;\n"); + WRITE(p, " if (patch_pos.x == (num_patches.x - 1))\n"); + WRITE(p, " knot[4].x = num_patches.x;\n"); + WRITE(p, " }\n"); + // WRITE(p, " if ((type.y & 1) != 0) {\n"); + WRITE(p, " if ((type.y == 1) || (type.y == 3)) {\n"); + WRITE(p, " if (patch_pos.y <= 2)\n"); + WRITE(p, " knot[0].y = 0.0;\n"); + WRITE(p, " if (patch_pos.y <= 1)\n"); + WRITE(p, " knot[1].y = 0.0;\n"); + WRITE(p, " }\n"); + // WRITE(p, " if ((type.y & 2) != 0) {\n"); + WRITE(p, " if ((type.y == 2) || (type.y == 3)) {\n"); + WRITE(p, " if (patch_pos.y >= (num_patches.y - 2))\n"); + WRITE(p, " knot[5].y = num_patches.y;\n"); + WRITE(p, " if (patch_pos.y == (num_patches.y - 1))\n"); + WRITE(p, " knot[4].y = num_patches.y;\n"); + WRITE(p, " }\n"); + WRITE(p, "}\n"); + + WRITE(p, "void spline_weight(float2 t, in float2 knot[6], out float2 weights[4]) {\n"); + // TODO: Maybe compilers could be coaxed into vectorizing this code without the above explicitly... + WRITE(p, " float2 t0 = (t - knot[0]);\n"); + WRITE(p, " float2 t1 = (t - knot[1]);\n"); + WRITE(p, " float2 t2 = (t - knot[2]);\n"); + // TODO: All our knots are integers so we should be able to get rid of these divisions (How?) + WRITE(p, " float2 f30 = t0 / (knot[3] - knot[0]);\n"); + WRITE(p, " float2 f41 = t1 / (knot[4] - knot[1]);\n"); + WRITE(p, " float2 f52 = t2 / (knot[5] - knot[2]);\n"); + WRITE(p, " float2 f31 = t1 / (knot[3] - knot[1]);\n"); + WRITE(p, " float2 f42 = t2 / (knot[4] - knot[2]);\n"); + WRITE(p, " float2 f32 = t2 / (knot[3] - knot[2]);\n"); + WRITE(p, " float2 a = (1.0 - f30)*(1.0 - f31);\n"); + WRITE(p, " float2 b = (f31*f41);\n"); + WRITE(p, " float2 c = (1.0 - f41)*(1.0 - f42);\n"); + WRITE(p, " float2 d = (f42*f52);\n"); + WRITE(p, " weights[0] = a - (a*f32);\n"); + WRITE(p, " weights[1] = 1.0 - a - b + ((a + b + c - 1.0)*f32);\n"); + WRITE(p, " weights[2] = b + ((1.0 - b - c - d)*f32);\n"); + WRITE(p, " weights[3] = d*f32;\n"); + WRITE(p, "}\n"); + } + } + WRITE(p, "VS_OUT main(VS_IN In) {\n"); WRITE(p, " VS_OUT Out;\n"); if (!useHWTransform) { @@ -300,12 +391,114 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l } else { // Step 1: World Transform / Skinning if (!enableBones) { - // No skinning, just standard T&L. - WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n"); - if (hasNormal) - WRITE(p, " float3 worldnormal = normalize(mul(float4(%sIn.normal, 0.0), u_world));\n", flipNormal ? "-" : ""); - else - WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); + // Hardware tessellation + if (doSpline || doBezier) { + WRITE(p, " uint num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3u" : "u_spline_count_u - 3"); + WRITE(p, " float2 tess_pos = In.position.xy;\n"); + WRITE(p, " int u = In.instanceId %% num_patches_u;\n"); + WRITE(p, " int v = In.instanceId / num_patches_u;\n"); + WRITE(p, " int2 patch_pos = int2(u, v);\n"); + WRITE(p, " float3 _pos[16];\n"); + WRITE(p, " float2 _tex[16];\n"); + WRITE(p, " float4 _col[16];\n"); + WRITE(p, " int idx;\n"); + WRITE(p, " int2 index;\n"); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + WRITE(p, " idx = (%i + v%s) * u_spline_count_u + (%i + u%s);\n", i, doBezier ? " * 3" : "", j, doBezier ? " * 3" : ""); + WRITE(p, " index = int2(idx, 0);\n"); + WRITE(p, " _pos[%i] = u_tess_pos_tex.Load(index).xyz;\n", i * 4 + j); + if (doTexture && hasTexcoord && hasTexcoordTess) + WRITE(p, " _tex[%i] = u_tess_tex_tex.Load(index).xy;\n", i * 4 + j); + if (hasColor && hasColorTess) + WRITE(p, " _col[%i] = u_tess_col_tex.Load(index).rgba;\n", i * 4 + j); + } + } + WRITE(p, " float2 weights[4];\n"); + if (doBezier) { + // Bernstein 3D + WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); + WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n"); + WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n"); + WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); + } else if (doSpline) { + WRITE(p, " int2 spline_num_patches = int2(u_spline_count_u - 3, u_spline_count_v - 3);\n"); + WRITE(p, " int2 spline_type = int2(u_spline_type_u, u_spline_type_v);\n"); + WRITE(p, " float2 knots[6];\n"); + WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); + WRITE(p, " spline_weight(tess_pos + patch_pos, knots, weights);\n"); + } + WRITE(p, " float3 pos = tess_sample(_pos, weights);\n"); + if (doTexture && hasTexcoord) { + if (hasTexcoordTess) + WRITE(p, " float2 tex = tess_sample(_tex, weights);\n"); + else + WRITE(p, " float2 tex = tess_pos + patch_pos;\n"); + } + if (hasColor) { + if (hasColorTess) + WRITE(p, " float4 col = tess_sample(_col, weights);\n"); + else + WRITE(p, " float4 col = u_tess_col_tex.Load(int2(0, 0)).rgba;\n"); + } + if (hasNormal) { + // Curved surface is probably always need to compute normal(not sampling from control points) + if (doBezier) { + // Bernstein derivative + WRITE(p, " float2 bernderiv[4];\n"); + WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n"); + WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n"); + WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n"); + WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n"); + + WRITE(p, " float2 bernderiv_u[4];\n"); + WRITE(p, " float2 bernderiv_v[4];\n"); + WRITE(p, " for (int i = 0; i < 4; i++) {\n"); + WRITE(p, " bernderiv_u[i] = float2(bernderiv[i].x, weights[i].y);\n"); + WRITE(p, " bernderiv_v[i] = float2(weights[i].x, bernderiv[i].y);\n"); + WRITE(p, " }\n"); + + WRITE(p, " float3 du = tess_sample(_pos, bernderiv_u);\n"); + WRITE(p, " float3 dv = tess_sample(_pos, bernderiv_v);\n"); + } else if (doSpline) { + WRITE(p, " float2 tess_next_u = float2(In.normal.x, 0.0);\n"); + WRITE(p, " float2 tess_next_v = float2(0.0, In.normal.y);\n"); + // Right + WRITE(p, " float2 tess_pos_r = tess_pos + tess_next_u;\n"); + WRITE(p, " spline_weight(tess_pos_r + patch_pos, knots, weights);\n"); + WRITE(p, " float3 pos_r = tess_sample(_pos, weights);\n"); + // Left + WRITE(p, " float2 tess_pos_l = tess_pos - tess_next_u;\n"); + WRITE(p, " spline_weight(tess_pos_l + patch_pos, knots, weights);\n"); + WRITE(p, " float3 pos_l = tess_sample(_pos, weights);\n"); + // Down + WRITE(p, " float2 tess_pos_d = tess_pos + tess_next_v;\n"); + WRITE(p, " spline_weight(tess_pos_d + patch_pos, knots, weights);\n"); + WRITE(p, " float3 pos_d = tess_sample(_pos, weights);\n"); + // Up + WRITE(p, " float2 tess_pos_u = tess_pos - tess_next_v;\n"); + WRITE(p, " spline_weight(tess_pos_u + patch_pos, knots, weights);\n"); + WRITE(p, " float3 pos_u = tess_sample(_pos, weights);\n"); + + WRITE(p, " float3 du = pos_r - pos_l;\n"); + WRITE(p, " float3 dv = pos_d - pos_u;\n"); + } + WRITE(p, " float3 nrm = cross(du, dv);\n"); + WRITE(p, " nrm = normalize(nrm);\n"); + } + WRITE(p, " float3 worldpos = mul(float4(pos.xyz, 1.0), u_world);\n"); + if (hasNormal) + WRITE(p, " float3 worldnormal = normalize(mul(float4(%snrm, 0.0), u_world));\n", flipNormalTess ? "-" : ""); + else + WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); + } else { + // No skinning, just standard T&L. + WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n"); + if (hasNormal) + WRITE(p, " float3 worldnormal = normalize(mul(float4(%sIn.normal, 0.0), u_world));\n", flipNormal ? "-" : ""); + else + WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n"); + } } else { static const char * const boneWeightAttr[8] = { "a_w1.x", "a_w1.y", "a_w1.z", "a_w1.w", @@ -399,6 +592,11 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l const char *ambientStr = (gstate.materialupdate & 1) && hasColor ? "In.color0" : "u_matambientalpha"; const char *diffuseStr = (gstate.materialupdate & 2) && hasColor ? "In.color0.rgb" : "u_matdiffuse"; const char *specularStr = (gstate.materialupdate & 4) && hasColor ? "In.color0.rgb" : "u_matspecular.rgb"; + if (doBezier || doSpline) { + ambientStr = (matUpdate & 1) && hasColor ? "col" : "u_matambientalpha"; + diffuseStr = (matUpdate & 2) && hasColor ? "col.rgb" : "u_matdiffuse"; + specularStr = (matUpdate & 4) && hasColor ? "col.rgb" : "u_matspecular.rgb"; + } bool diffuseIsZero = true; bool specularIsZero = true; @@ -514,7 +712,10 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l } else { // Lighting doesn't affect color. if (hasColor) { - WRITE(p, " Out.v_color0 = In.color0;\n"); + if (doBezier || doSpline) + WRITE(p, " Out.v_color0 = col;\n"); + else + WRITE(p, " Out.v_color0 = In.color0;\n"); } else { WRITE(p, " Out.v_color0 = u_matambientalpha;\n"); } @@ -529,13 +730,19 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l case GE_TEXMAP_UNKNOWN: // Not sure what this is, but Riviera uses it. Treating as coords works. if (scaleUV) { if (hasTexcoord) { - WRITE(p, " Out.v_texcoord = float3(In.texcoord.xy * u_uvscaleoffset.xy, 0.0);\n"); + if (doBezier || doSpline) + WRITE(p, " Out.v_texcoord = float3(tex.xy * u_uvscaleoffset.xy + u_uvscaleoffset.zw, 0.0);\n"); + else + WRITE(p, " Out.v_texcoord = float3(In.texcoord.xy * u_uvscaleoffset.xy, 0.0);\n"); } else { WRITE(p, " Out.v_texcoord = float3(0.0, 0.0, 0.0);\n"); } } else { if (hasTexcoord) { - WRITE(p, " Out.v_texcoord = float3(In.texcoord.xy * u_uvscaleoffset.xy + u_uvscaleoffset.zw, 0.0);\n"); + if (doBezier || doSpline) + WRITE(p, " Out.v_texcoord = float3(tex.xy * u_uvscaleoffset.xy + u_uvscaleoffset.zw, 0.0);\n"); + else + WRITE(p, " Out.v_texcoord = float3(In.texcoord.xy * u_uvscaleoffset.xy + u_uvscaleoffset.zw, 0.0);\n"); } else { WRITE(p, " Out.v_texcoord = float3(u_uvscaleoffset.zw, 0.0);\n"); } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 7e44b1d940..c289b86751 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -99,6 +99,17 @@ bool CheckSupportInstancedTessellationGLES() { #endif } +bool IsBackendSupportHWTess() { + switch (g_Config.iGPUBackend) { + case GPU_BACKEND_OPENGL: + return CheckSupportInstancedTessellationGLES(); + case GPU_BACKEND_VULKAN: + case GPU_BACKEND_DIRECT3D11: + return true; + } + return false; +} + void GameSettingsScreen::CreateViews() { GameInfo *info = g_gameInfoCache->GetInfo(NULL, gamePath_, GAMEINFO_WANTBG | GAMEINFO_WANTSIZE); @@ -312,9 +323,7 @@ void GameSettingsScreen::CreateViews() { bezierChoiceDisable_ = g_Config.bSoftwareRendering || g_Config.bHardwareTessellation; return UI::EVENT_CONTINUE; }); - bool isBackendSupportHWTess = g_Config.iGPUBackend == GPU_BACKEND_OPENGL || g_Config.iGPUBackend == GPU_BACKEND_VULKAN; - bool isDeviceSupportInstTess = g_Config.iGPUBackend == GPU_BACKEND_OPENGL ? CheckSupportInstancedTessellationGLES() : isBackendSupportHWTess; - tessHWEnable_ = isDeviceSupportInstTess && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; + tessHWEnable_ = IsBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; tessellationHW->SetEnabledPtr(&tessHWEnable_); // In case we're going to add few other antialiasing option like MSAA in the future. @@ -754,17 +763,13 @@ UI::EventReturn GameSettingsScreen::OnSoftwareRendering(UI::EventParams &e) { postProcEnable_ = !g_Config.bSoftwareRendering && (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE); resolutionEnable_ = !g_Config.bSoftwareRendering && (g_Config.iRenderingMode != FB_NON_BUFFERED_MODE); bezierChoiceDisable_ = g_Config.bSoftwareRendering || g_Config.bHardwareTessellation; - bool isBackendSupportHWTess = g_Config.iGPUBackend == GPU_BACKEND_OPENGL || g_Config.iGPUBackend == GPU_BACKEND_VULKAN; - bool isDeviceSupportInstTess = g_Config.iGPUBackend == GPU_BACKEND_OPENGL ? CheckSupportInstancedTessellationGLES() : isBackendSupportHWTess; - tessHWEnable_ = isDeviceSupportInstTess && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; + tessHWEnable_ = IsBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; return UI::EVENT_DONE; } UI::EventReturn GameSettingsScreen::OnHardwareTransform(UI::EventParams &e) { vtxCacheEnable_ = !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; - bool isBackendSupportHWTess = g_Config.iGPUBackend == GPU_BACKEND_OPENGL || g_Config.iGPUBackend == GPU_BACKEND_VULKAN; - bool isDeviceSupportInstTess = g_Config.iGPUBackend == GPU_BACKEND_OPENGL ? CheckSupportInstancedTessellationGLES() : isBackendSupportHWTess; - tessHWEnable_ = isDeviceSupportInstTess && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; + tessHWEnable_ = IsBackendSupportHWTess() && !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; return UI::EVENT_DONE; }