From 65be0aa002c5618a947a2d88b3f05f76603f1cb9 Mon Sep 17 00:00:00 2001 From: xebra Date: Fri, 6 Jan 2017 21:07:19 +0900 Subject: [PATCH 01/17] [spline/bezier]Move index conversion logic to tiny class. --- GPU/Common/SplineCommon.cpp | 65 +++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index a985d44d12..64ddc37945 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -781,6 +781,31 @@ void TesselateBezierPatch(u8 *&dest, u16 *&indices, int &count, int tess_u, int } } +class IndexConverter { +private: + union { + const void *indices; + const u8 *indices8; + const u16 *indices16; + const u32 *indices32; + }; + u32 indexType; +public: + IndexConverter(u32 vertType, const void *indices) : indices(indices), indexType(vertType & GE_VTYPE_IDX_MASK) {} + + inline u32 convert(u32 index) const { + switch (indexType) { + case GE_VTYPE_IDX_8BIT: + return indices8[index]; + case GE_VTYPE_IDX_16BIT: + return indices16[index]; + case GE_VTYPE_IDX_32BIT: + return indices32[index]; + } + return index; + } +}; + // This maps GEPatchPrimType to GEPrimitiveType. const GEPrimitiveType primType[] = { GE_PRIM_TRIANGLES, GE_PRIM_LINES, GE_PRIM_POINTS, GE_PRIM_POINTS }; @@ -790,11 +815,7 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi u16 index_lower_bound = 0; u16 index_upper_bound = count_u * count_v - 1; - bool indices_16bit = (vertType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_16BIT; - bool indices_32bit = (vertType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_32BIT; - const u8 *indices8 = (const u8 *)indices; - const u16 *indices16 = (const u16 *)indices; - const u32 *indices32 = (const u32 *)indices; + IndexConverter idxConv(vertType, indices); if (indices) GetIndexBounds(indices, count_u * count_v, vertType, &index_lower_bound, &index_upper_bound); @@ -825,19 +846,7 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi // Make an array of pointers to the control points, to get rid of indices. for (int idx = 0; idx < count_u * count_v; idx++) { - if (indices) { - u32 ind; - if (indices_32bit) { - ind = indices32[idx]; - } else if (indices_16bit) { - ind = indices16[idx]; - } else { - ind = indices8[idx]; - } - points[idx] = simplified_control_points + ind; - } else { - points[idx] = simplified_control_points + idx; - } + points[idx] = simplified_control_points + (indices ? idxConv.convert(idx) : idx); } int count = 0; @@ -890,11 +899,7 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi u16 index_lower_bound = 0; u16 index_upper_bound = count_u * count_v - 1; - bool indices_16bit = (vertType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_16BIT; - bool indices_32bit = (vertType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_32BIT; - const u8 *indices8 = (const u8 *)indices; - const u16 *indices16 = (const u16 *)indices; - const u32 *indices32 = (const u32 *)indices; + IndexConverter idxConv(vertType, indices); if (indices) GetIndexBounds(indices, count_u*count_v, vertType, &index_lower_bound, &index_upper_bound); @@ -931,19 +936,7 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi BezierPatch& patch = patches[patch_u + patch_v * num_patches_u]; for (int point = 0; point < 16; ++point) { int idx = (patch_u * 3 + point % 4) + (patch_v * 3 + point / 4) * count_u; - if (indices) { - u32 ind; - if (indices_32bit) { - ind = indices32[idx]; - } else if (indices_16bit) { - ind = indices16[idx]; - } else { - ind = indices8[idx]; - } - patch.points[point] = simplified_control_points + ind; - } else { - patch.points[point] = simplified_control_points + idx; - } + patch.points[point] = simplified_control_points + (indices ? idxConv.convert(idx) : idx); } patch.u_index = patch_u * 3; patch.v_index = patch_v * 3; From 192d8f3601b71f896bef60838a176d14230c185a Mon Sep 17 00:00:00 2001 From: xebra Date: Sat, 7 Jan 2017 00:14:58 +0900 Subject: [PATCH 02/17] [spline/bezier]Add an option for hardware tessellation. --- Core/Config.cpp | 1 + Core/Config.h | 1 + UI/GameSettingsScreen.cpp | 3 +++ 3 files changed, 5 insertions(+) diff --git a/Core/Config.cpp b/Core/Config.cpp index 26786f3945..0a192d797b 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -493,6 +493,7 @@ static ConfigSetting graphicsSettings[] = { // Not really a graphics setting... ReportedConfigSetting("TimerHack", &g_Config.bTimerHack, &DefaultTimerHack, true, true), ReportedConfigSetting("SplineBezierQuality", &g_Config.iSplineBezierQuality, 2, true, true), + ReportedConfigSetting("HardwareTessellation", &g_Config.bHardwareTessellation, false, true, true), ReportedConfigSetting("PostShader", &g_Config.sPostShaderName, "Off", true, true), ReportedConfigSetting("MemBlockTransferGPU", &g_Config.bBlockTransferGPU, true, true, true), diff --git a/Core/Config.h b/Core/Config.h index d420c58577..d76d1b2519 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -212,6 +212,7 @@ public: bool bDisableSlowFramebufEffects; bool bFragmentTestCache; int iSplineBezierQuality; // 0 = low , 1 = Intermediate , 2 = High + bool bHardwareTessellation; std::string sPostShaderName; // Off for off. bool bGfxDebugOutput; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 3b90519980..0de0fbada4 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -280,6 +280,9 @@ void GameSettingsScreen::CreateViews() { }); beziersChoice->SetDisabledPtr(&g_Config.bSoftwareRendering); + CheckBox *tessellationHW = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTessellation, gr->T("Hardware Tessellation"))); + tessellationHW->SetEnabledPtr(&vtxCacheEnable_); // Same as Vertex Cache(!g_Config.bSoftwareRendering && g_Config.bHardwareTransform) + // In case we're going to add few other antialiasing option like MSAA in the future. // graphicsSettings->Add(new CheckBox(&g_Config.bFXAA, gr->T("FXAA"))); graphicsSettings->Add(new ItemHeader(gr->T("Texture Scaling"))); From b78e184c51b5171c22c421f3c07326985ecf425d Mon Sep 17 00:00:00 2001 From: xebra Date: Sat, 7 Jan 2017 01:19:09 +0900 Subject: [PATCH 03/17] [spline/bezier]Add some variables in gpu state cache for hardware tessellation. --- GPU/GPUState.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 5d4c3f006e..826dc84041 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -395,6 +395,7 @@ struct GPUgstate { bool isSkinningEnabled() const { return ((vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE); } GEPatchPrimType getPatchPrimitiveType() const { return static_cast(patchprimitive & 3); } + bool isPatchNormalsReversed() const { return patchfacing & 1; } // Transfers u32 getTransferSrcAddress() const { return (transfersrc & 0xFFFFF0) | ((transfersrcw & 0xFF0000) << 8); } @@ -502,6 +503,14 @@ struct GPUStateCache { UVScale uv; + bool bezier; + bool spline; + int bezier_count_u; + int spline_count_u; + int spline_count_v; + int spline_type_u; + int spline_type_v; + bool bgraTexture; bool needShaderTexClamp; bool allowShaderBlend; From 71a44644a1d49681c1a3c6c6ac8c45239101130e Mon Sep 17 00:00:00 2001 From: xebra Date: Sat, 7 Jan 2017 01:21:17 +0900 Subject: [PATCH 04/17] [spline/bezier]Add some flags in shader id for hardware tessellation. --- GPU/Common/ShaderId.cpp | 18 ++++++++++++++++++ GPU/Common/ShaderId.h | 12 +++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index 3558df799f..beb0eaabe0 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -45,6 +45,12 @@ std::string VertexShaderDesc(const ShaderID &id) { if (id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2)) desc << "WScale " << id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2) << " "; if (id.Bit(VS_BIT_FLATSHADE)) desc << "Flat "; + if (id.Bit(VS_BIT_BEZIER)) desc << "Bezier "; + if (id.Bit(VS_BIT_SPLINE)) desc << "Spline "; + if (id.Bit(VS_BIT_HAS_COLOR_TESS)) desc << "TessC "; + if (id.Bit(VS_BIT_HAS_TEXCOORD_TESS)) desc << "TessT "; + if (id.Bit(VS_BIT_NORM_REVERSE_TESS)) desc << "TessRevN "; + // TODO: More... return desc.str(); @@ -59,6 +65,12 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform) bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0; bool hasNormal = (vertType & GE_VTYPE_NRM_MASK) != 0; bool hasTexcoord = (vertType & GE_VTYPE_TC_MASK) != 0; + + bool doBezier = gstate_c.bezier; + bool doSpline = gstate_c.spline; + bool hasColorTess = (gstate.vertType & GE_VTYPE_COL_MASK) != 0 && (doBezier || doSpline); + bool hasTexcoordTess = (gstate.vertType & GE_VTYPE_TC_MASK) != 0 && (doBezier || doSpline); + bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear(); bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled(); // lmode: && !isModeThrough!? @@ -119,6 +131,12 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform) id.SetBit(VS_BIT_NORM_REVERSE, gstate.areNormalsReversed()); id.SetBit(VS_BIT_HAS_TEXCOORD, hasTexcoord); + + id.SetBit(VS_BIT_BEZIER, doBezier); + id.SetBit(VS_BIT_SPLINE, doSpline); + id.SetBit(VS_BIT_HAS_COLOR_TESS, hasColorTess); + id.SetBit(VS_BIT_HAS_TEXCOORD_TESS, hasTexcoordTess); + id.SetBit(VS_BIT_NORM_REVERSE_TESS, gstate.isPatchNormalsReversed()); } id.SetBit(VS_BIT_FLATSHADE, doFlatShading); diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 6c6f32aa6c..599b4cadc8 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -15,7 +15,10 @@ enum { VS_BIT_USE_HW_TRANSFORM = 8, VS_BIT_HAS_NORMAL = 9, // conditioned on hw transform VS_BIT_NORM_REVERSE = 10, - VS_BIT_HAS_TEXCOORD = 11, // 5 free after + VS_BIT_HAS_TEXCOORD = 11, + VS_BIT_HAS_COLOR_TESS = 12, // 1 bit + VS_BIT_HAS_TEXCOORD_TESS = 13, // 1 bit + VS_BIT_NORM_REVERSE_TESS = 14, // 1 bit 1 free after VS_BIT_UVGEN_MODE = 16, VS_BIT_UVPROJ_MODE = 18, // 2, can overlap with LS0 VS_BIT_LS0 = 18, // 2 @@ -30,14 +33,17 @@ enum { VS_BIT_LIGHT2_TYPE = 42, // 2 bits VS_BIT_LIGHT3_COMP = 44, // 2 bits VS_BIT_LIGHT3_TYPE = 46, // 2 bits - VS_BIT_MATERIAL_UPDATE = 48, // 3 bits, 1 free after + VS_BIT_MATERIAL_UPDATE = 48, // 3 bits + VS_BIT_SPLINE = 51, // 1 bit VS_BIT_LIGHT0_ENABLE = 52, VS_BIT_LIGHT1_ENABLE = 53, VS_BIT_LIGHT2_ENABLE = 54, VS_BIT_LIGHT3_ENABLE = 55, VS_BIT_LIGHTING_ENABLE = 56, VS_BIT_WEIGHT_FMTSCALE = 57, // only two bits, 1 free after - VS_BIT_FLATSHADE = 62, // 1 free after + VS_BIT_FLATSHADE = 62, // 1 bit + VS_BIT_BEZIER = 63, // 1 bit + // No more free }; From 52f86cf61b935ab4104fb9e62c9e37f39d5796a8 Mon Sep 17 00:00:00 2001 From: xebra Date: Sun, 8 Jan 2017 22:14:35 +0900 Subject: [PATCH 05/17] [spline/bezier]Implement common logic of hardware tessellation. --- GPU/Common/DrawEngineCommon.h | 12 +++ GPU/Common/SplineCommon.cpp | 151 +++++++++++++++++++++++++++++----- 2 files changed, 143 insertions(+), 20 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 0c42ddec33..0e7d89457e 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -87,4 +87,16 @@ protected: // Fixed index buffer for easy quad generation from spline/bezier u16 *quadIndices_; + + // Hardware tessellation + int numPatches; + class TessellationDataTransfer { + protected: + int prevSize; + int prevSizeTex; + int prevSizeCol; + public: + virtual void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) = 0; + }; + TessellationDataTransfer *tessDataTransfer; }; diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 64ddc37945..3745ff1fac 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -223,6 +223,39 @@ static void spline_knot(int n, int type, float *knot) { } } +static void TessellateSplinePatchHardware(u8 *&dest, u16 *indices, int &count, const SplinePatchLocal &spatch) { + SimpleVertex *&vertices = (SimpleVertex*&)dest; + + for (int tile_v = 0; tile_v < spatch.tess_v + 1; ++tile_v) { + for (int tile_u = 0; tile_u < spatch.tess_u + 1; ++tile_u) { + float u = ((float)tile_u / (float)spatch.tess_u); + float v = ((float)tile_v / (float)spatch.tess_v); + + SimpleVertex &vert = vertices[tile_v * (spatch.tess_u + 1) + tile_u]; + + vert.pos.x = u; + vert.pos.y = v; + + // For compute normal + vert.nrm.x = 1.0f / (float)spatch.tess_u; + vert.nrm.y = 1.0f / (float)spatch.tess_v; + } + } + + // Combine the vertices into triangles. + for (int tile_v = 0; tile_v < spatch.tess_v; ++tile_v) { + for (int tile_u = 0; tile_u < spatch.tess_u; ++tile_u) { + int idx0 = tile_v * (spatch.tess_u + 1) + tile_u; + int idx1 = tile_v * (spatch.tess_u + 1) + tile_u + 1; + int idx2 = (tile_v + 1) * (spatch.tess_u + 1) + tile_u; + int idx3 = (tile_v + 1) * (spatch.tess_u + 1) + tile_u + 1; + + CopyQuadIndex(indices, spatch.primType, idx0, idx1, idx2, idx3); + count += 6; + } + } +} + static void _SplinePatchLowQuality(u8 *&dest, u16 *indices, int &count, const SplinePatchLocal &spatch, u32 origVertType) { // Fast and easy way - just draw the control points, generate some very basic normal vector substitutes. // Very inaccurate but okay for Loco Roco. Maybe should keep it as an option because it's fast. @@ -767,6 +800,35 @@ static void _BezierPatchHighQuality(u8 *&dest, u16 *&indices, int &count, int te dest += (tess_u + 1) * (tess_v + 1) * sizeof(SimpleVertex); } +static void TesselateBezierPatchHardware(u8 *&dest, u16 *indices, int &count, int tess_u, int tess_v, GEPatchPrimType primType) { + SimpleVertex *&vertices = (SimpleVertex*&)dest; + + for (int tile_v = 0; tile_v < tess_v + 1; ++tile_v) { + for (int tile_u = 0; tile_u < tess_u + 1; ++tile_u) { + float u = ((float)tile_u / (float)tess_u); + float v = ((float)tile_v / (float)tess_v); + + SimpleVertex &vert = vertices[tile_v * (tess_u + 1) + tile_u]; + + vert.pos.x = u; + vert.pos.y = v; + } + } + + // Combine the vertices into triangles. + for (int tile_v = 0; tile_v < tess_v; ++tile_v) { + for (int tile_u = 0; tile_u < tess_u; ++tile_u) { + int idx0 = tile_v * (tess_u + 1) + tile_u; + int idx1 = tile_v * (tess_u + 1) + tile_u + 1; + int idx2 = (tile_v + 1) * (tess_u + 1) + tile_u; + int idx3 = (tile_v + 1) * (tess_u + 1) + tile_u + 1; + + CopyQuadIndex(indices, primType, idx0, idx1, idx2, idx3); + count += 6; + } + } +} + void TesselateBezierPatch(u8 *&dest, u16 *&indices, int &count, int tess_u, int tess_v, const BezierPatch &patch, u32 origVertType, int maxVertices) { switch (g_Config.iSplineBezierQuality) { case LOW_QUALITY: @@ -865,9 +927,30 @@ void DrawEngineCommon::SubmitSpline(const void *control_points, const void *indi patch.primType = prim_type; patch.patchFacing = patchFacing; - int maxVertexCount = SPLINE_BUFFER_SIZE / vertexSize; - TesselateSplinePatch(dest, quadIndices_, count, patch, origVertType, maxVertexCount); + if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + float *pos = (float*)(decoded + 65536 * 18); // Size 3 float + float *tex = pos + count_u * count_v * 3; // Size 3 float + float *col = tex + count_u * count_v * 3; // Size 4 float + const bool hasColor = (origVertType & GE_VTYPE_COL_MASK) != 0; + const bool hasTexCoords = (origVertType & GE_VTYPE_TC_MASK) != 0; + for (int idx = 0; idx < count_u * count_v; idx++) { + memcpy(pos + idx * 3, points[idx]->pos.AsArray(), 3 * sizeof(float)); + if (hasTexCoords) + memcpy(tex + idx * 3, points[idx]->uv, 2 * sizeof(float)); + if (hasColor) + memcpy(col + idx * 4, Vec4f::FromRGBA(points[idx]->color_32).AsArray(), 4 * sizeof(float)); + } + if (!hasColor) + memcpy(col, Vec4f::FromRGBA(points[0]->color_32).AsArray(), 4 * sizeof(float)); + + tessDataTransfer->SendDataToShader(pos, tex, col, count_u * count_v, hasColor, hasTexCoords); + TessellateSplinePatchHardware(dest, quadIndices_, count, patch); + numPatches = (count_u - 3) * (count_v - 3); + } else { + int maxVertexCount = SPLINE_BUFFER_SIZE / vertexSize; + TesselateSplinePatch(dest, quadIndices_, count, patch, origVertType, maxVertexCount); + } delete[] points; u32 vertTypeWithIndex16 = (vertType & ~GE_VTYPE_IDX_MASK) | GE_VTYPE_IDX_16BIT; @@ -927,23 +1010,45 @@ 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 3 float + float *tex = pos + count_u * count_v * 3; // Size 3 float + float *col = tex + count_u * count_v * 3; // Size 4 float + const bool hasColor = (origVertType & GE_VTYPE_COL_MASK) != 0; + const bool hasTexCoords = (origVertType & GE_VTYPE_TC_MASK) != 0; + // Bezier patches share less control points than spline patches. Otherwise they are pretty much the same (except bezier don't support the open/close thing) int num_patches_u = (count_u - 1) / 3; int num_patches_v = (count_v - 1) / 3; - BezierPatch *patches = new BezierPatch[num_patches_u * num_patches_v]; - for (int patch_u = 0; patch_u < num_patches_u; patch_u++) { - for (int patch_v = 0; patch_v < num_patches_v; patch_v++) { - BezierPatch& patch = patches[patch_u + patch_v * num_patches_u]; - for (int point = 0; point < 16; ++point) { - int idx = (patch_u * 3 + point % 4) + (patch_v * 3 + point / 4) * count_u; - patch.points[point] = simplified_control_points + (indices ? idxConv.convert(idx) : idx); + BezierPatch *patches; + if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + for (int idx = 0; idx < count_u * count_v; idx++) { + SimpleVertex *point = simplified_control_points + (indices ? idxConv.convert(idx) : idx); + memcpy(pos + idx * 3, point->pos.AsArray(), 3 * sizeof(float)); + if (hasTexCoords) + memcpy(tex + idx * 3, point->uv, 2 * sizeof(float)); + if (hasColor) + memcpy(col + idx * 4, Vec4f::FromRGBA(point->color_32).AsArray(), 4 * sizeof(float)); + } + if (!hasColor) { + SimpleVertex *point = simplified_control_points + (indices ? idxConv.convert(0) : 0); + memcpy(col, Vec4f::FromRGBA(point->color_32).AsArray(), 4 * sizeof(float)); + } + } else { + patches = new BezierPatch[num_patches_u * num_patches_v]; + for (int patch_u = 0; patch_u < num_patches_u; patch_u++) { + for (int patch_v = 0; patch_v < num_patches_v; patch_v++) { + BezierPatch& patch = patches[patch_u + patch_v * num_patches_u]; + for (int point = 0; point < 16; ++point) { + int idx = (patch_u * 3 + point % 4) + (patch_v * 3 + point / 4) * count_u; + patch.points[point] = simplified_control_points + (indices ? idxConv.convert(idx) : idx); + } + patch.u_index = patch_u * 3; + patch.v_index = patch_v * 3; + patch.index = patch_v * num_patches_u + patch_u; + patch.primType = prim_type; + patch.computeNormals = computeNormals; + patch.patchFacing = patchFacing; } - patch.u_index = patch_u * 3; - patch.v_index = patch_v * 3; - patch.index = patch_v * num_patches_u + patch_u; - patch.primType = prim_type; - patch.computeNormals = computeNormals; - patch.patchFacing = patchFacing; } } @@ -962,12 +1067,18 @@ void DrawEngineCommon::SubmitBezier(const void *control_points, const void *indi } u16 *inds = quadIndices_; - int maxVertices = SPLINE_BUFFER_SIZE / vertexSize; - for (int patch_idx = 0; patch_idx < num_patches_u*num_patches_v; ++patch_idx) { - const BezierPatch &patch = patches[patch_idx]; - TesselateBezierPatch(dest, inds, count, tess_u, tess_v, patch, origVertType, maxVertices); + if (g_Config.bHardwareTessellation && g_Config.bHardwareTransform && !g_Config.bSoftwareRendering) { + tessDataTransfer->SendDataToShader(pos, tex, col, count_u * count_v, hasColor, hasTexCoords); + TesselateBezierPatchHardware(dest, inds, count, tess_u, tess_v, prim_type); + numPatches = num_patches_u * num_patches_v; + } else { + int maxVertices = SPLINE_BUFFER_SIZE / vertexSize; + for (int patch_idx = 0; patch_idx < num_patches_u*num_patches_v; ++patch_idx) { + const BezierPatch &patch = patches[patch_idx]; + TesselateBezierPatch(dest, inds, count, tess_u, tess_v, patch, origVertType, maxVertices); + } + delete[] patches; } - delete[] patches; u32 vertTypeWithIndex16 = (vertType & ~GE_VTYPE_IDX_MASK) | GE_VTYPE_IDX_16BIT; From 38293adc7862c7794c032aececbab32419fd5837 Mon Sep 17 00:00:00 2001 From: xebra Date: Sun, 8 Jan 2017 23:01:01 +0900 Subject: [PATCH 06/17] [spline/bezier]Expand shader dirty flags to 64 bit, and add some flags for hardware tessellation. --- GPU/GLES/ShaderManager.cpp | 10 +-- GPU/GLES/ShaderManager.h | 124 +++++++++++++++++++++++++------------ 2 files changed, 91 insertions(+), 43 deletions(-) diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index d0fb2c4a32..0033b5c78e 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -413,7 +413,7 @@ void LinkedShader::stop() { } void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { - u32 dirty = dirtyUniforms & availableUniforms; + u64 dirty = dirtyUniforms & availableUniforms; dirtyUniforms = 0; if (!dirty) return; @@ -707,7 +707,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { } ShaderManager::ShaderManager() - : lastShader_(nullptr), globalDirty_(0xFFFFFFFF), shaderSwitchDirty_(0), diskCacheDirty_(false) { + : lastShader_(nullptr), globalDirty_(DIRTY_ALL), shaderSwitchDirty_(0), diskCacheDirty_(false) { codeBuffer_ = new char[16384]; lastFSID_.set_invalid(); lastVSID_.set_invalid(); @@ -731,7 +731,7 @@ void ShaderManager::Clear() { linkedShaderCache_.clear(); fsCache_.clear(); vsCache_.clear(); - globalDirty_ = 0xFFFFFFFF; + globalDirty_ = DIRTY_ALL; lastFSID_.set_invalid(); lastVSID_.set_invalid(); DirtyShader(); @@ -747,7 +747,7 @@ void ShaderManager::DirtyShader() { lastFSID_.set_invalid(); lastVSID_.set_invalid(); DirtyLastShader(); - globalDirty_ = 0xFFFFFFFF; + globalDirty_ = DIRTY_ALL; shaderSwitchDirty_ = 0; } @@ -847,7 +847,7 @@ LinkedShader *ShaderManager::ApplyFragmentShader(ShaderID VSID, Shader *vs, u32 // Okay, we have both shaders. Let's see if there's a linked one. LinkedShader *ls = nullptr; - u32 switchDirty = shaderSwitchDirty_; + u64 switchDirty = shaderSwitchDirty_; for (auto iter = linkedShaderCache_.begin(); iter != linkedShaderCache_.end(); ++iter) { // Deferred dirtying! Let's see if we can make this even more clever later. iter->ls->dirtyUniforms |= switchDirty; diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index 5a51f78332..0cc8ca5b99 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -55,8 +55,8 @@ public: bool useHWTransform_; uint32_t program; - u32 availableUniforms; - u32 dirtyUniforms; + u64 availableUniforms; + u64 dirtyUniforms; // Present attributes in the shader. int attrMask; // 1 << ATTR_ ... or-ed together. @@ -113,51 +113,99 @@ public: }; enum { - DIRTY_PROJMATRIX = (1 << 0), - DIRTY_PROJTHROUGHMATRIX = (1 << 1), - DIRTY_FOGCOLOR = (1 << 2), - DIRTY_FOGCOEF = (1 << 3), - DIRTY_TEXENV = (1 << 4), - DIRTY_ALPHACOLORREF = (1 << 5), + _DIRTY_PROJMATRIX = 0, + _DIRTY_PROJTHROUGHMATRIX = 1, + _DIRTY_FOGCOLOR = 2, + _DIRTY_FOGCOEF = 3, + _DIRTY_TEXENV = 4, + _DIRTY_ALPHACOLORREF = 5, // 1 << 6 is free! Wait, not anymore... - DIRTY_STENCILREPLACEVALUE = (1 << 6), + _DIRTY_STENCILREPLACEVALUE = 6, - DIRTY_ALPHACOLORMASK = (1 << 7), - DIRTY_LIGHT0 = (1 << 8), - DIRTY_LIGHT1 = (1 << 9), - DIRTY_LIGHT2 = (1 << 10), - DIRTY_LIGHT3 = (1 << 11), + _DIRTY_ALPHACOLORMASK = 7, + _DIRTY_LIGHT0 = 8, + _DIRTY_LIGHT1 = 9, + _DIRTY_LIGHT2 = 10, + _DIRTY_LIGHT3 = 11, - DIRTY_MATDIFFUSE = (1 << 12), - DIRTY_MATSPECULAR = (1 << 13), - DIRTY_MATEMISSIVE = (1 << 14), - DIRTY_AMBIENT = (1 << 15), - DIRTY_MATAMBIENTALPHA = (1 << 16), + _DIRTY_MATDIFFUSE = 12, + _DIRTY_MATSPECULAR = 13, + _DIRTY_MATEMISSIVE = 14, + _DIRTY_AMBIENT = 15, + _DIRTY_MATAMBIENTALPHA = 16, - DIRTY_SHADERBLEND = (1 << 17), // Used only for in-shader blending. + _DIRTY_SHADERBLEND = 17, // Used only for in-shader blending. - DIRTY_UVSCALEOFFSET = (1 << 18), // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" + _DIRTY_UVSCALEOFFSET = 18, // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" // Texclamp is fairly rare so let's share it's bit with DIRTY_DEPTHRANGE. - DIRTY_TEXCLAMP = (1 << 19), - DIRTY_DEPTHRANGE = (1 << 19), + _DIRTY_TEXCLAMP = 19, + _DIRTY_DEPTHRANGE = 19, - DIRTY_WORLDMATRIX = (1 << 21), - DIRTY_VIEWMATRIX = (1 << 22), // Maybe we'll fold this into projmatrix eventually - DIRTY_TEXMATRIX = (1 << 23), - DIRTY_BONEMATRIX0 = (1 << 24), - DIRTY_BONEMATRIX1 = (1 << 25), - DIRTY_BONEMATRIX2 = (1 << 26), - DIRTY_BONEMATRIX3 = (1 << 27), - DIRTY_BONEMATRIX4 = (1 << 28), - DIRTY_BONEMATRIX5 = (1 << 29), - DIRTY_BONEMATRIX6 = (1 << 30), - DIRTY_BONEMATRIX7 = (1 << 31), + _DIRTY_WORLDMATRIX = 21, + _DIRTY_VIEWMATRIX = 22, // Maybe we'll fold this into projmatrix eventually + _DIRTY_TEXMATRIX = 23, + _DIRTY_BONEMATRIX0 = 24, + _DIRTY_BONEMATRIX1 = 25, + _DIRTY_BONEMATRIX2 = 26, + _DIRTY_BONEMATRIX3 = 27, + _DIRTY_BONEMATRIX4 = 28, + _DIRTY_BONEMATRIX5 = 29, + _DIRTY_BONEMATRIX6 = 30, + _DIRTY_BONEMATRIX7 = 31, - DIRTY_ALL = 0xFFFFFFFF + _DIRTY_BEZIERCOUNTU = 32, // Used only for hardware tessellation + _DIRTY_SPLINECOUNTU = 33, // Used only for hardware tessellation + _DIRTY_SPLINECOUNTV = 34, // Used only for hardware tessellation + _DIRTY_SPLINETYPEU = 35, // Used only for hardware tessellation + _DIRTY_SPLINETYPEV = 36, // Used only for hardware tessellation + + __END_OF_LINE__DIRTY = 64, + + DIRTY_ALL = -1 }; +#define FLAG_BIT64(x) (1ULL << x) + +#define DIRTY_PROJMATRIX FLAG_BIT64(_DIRTY_PROJMATRIX) +#define DIRTY_PROJTHROUGHMATRIX FLAG_BIT64(_DIRTY_PROJTHROUGHMATRIX) +#define DIRTY_FOGCOLOR FLAG_BIT64(_DIRTY_FOGCOLOR) +#define DIRTY_FOGCOEF FLAG_BIT64(_DIRTY_FOGCOEF) +#define DIRTY_TEXENV FLAG_BIT64(_DIRTY_TEXENV) +#define DIRTY_ALPHACOLORREF FLAG_BIT64(_DIRTY_ALPHACOLORREF) +#define DIRTY_STENCILREPLACEVALUE FLAG_BIT64(_DIRTY_STENCILREPLACEVALUE) +#define DIRTY_ALPHACOLORMASK FLAG_BIT64(_DIRTY_ALPHACOLORMASK) +#define DIRTY_LIGHT0 FLAG_BIT64(_DIRTY_LIGHT0) +#define DIRTY_LIGHT1 FLAG_BIT64(_DIRTY_LIGHT1) +#define DIRTY_LIGHT2 FLAG_BIT64(_DIRTY_LIGHT2) +#define DIRTY_LIGHT3 FLAG_BIT64(_DIRTY_LIGHT3) +#define DIRTY_MATDIFFUSE FLAG_BIT64(_DIRTY_MATDIFFUSE) +#define DIRTY_MATSPECULAR FLAG_BIT64(_DIRTY_MATSPECULAR) +#define DIRTY_MATEMISSIVE FLAG_BIT64(_DIRTY_MATEMISSIVE) +#define DIRTY_AMBIENT FLAG_BIT64(_DIRTY_AMBIENT) +#define DIRTY_MATAMBIENTALPHA FLAG_BIT64(_DIRTY_MATAMBIENTALPHA) +#define DIRTY_SHADERBLEND FLAG_BIT64(_DIRTY_SHADERBLEND) +#define DIRTY_UVSCALEOFFSET FLAG_BIT64(_DIRTY_UVSCALEOFFSET) +#define DIRTY_TEXCLAMP FLAG_BIT64(_DIRTY_TEXCLAMP) +#define DIRTY_DEPTHRANGE FLAG_BIT64(_DIRTY_DEPTHRANGE) +#define DIRTY_WORLDMATRIX FLAG_BIT64(_DIRTY_WORLDMATRIX) +#define DIRTY_VIEWMATRIX FLAG_BIT64(_DIRTY_VIEWMATRIX) +#define DIRTY_TEXMATRIX FLAG_BIT64(_DIRTY_TEXMATRIX) +#define DIRTY_BONEMATRIX0 FLAG_BIT64(_DIRTY_BONEMATRIX0) +#define DIRTY_BONEMATRIX1 FLAG_BIT64(_DIRTY_BONEMATRIX1) +#define DIRTY_BONEMATRIX2 FLAG_BIT64(_DIRTY_BONEMATRIX2) +#define DIRTY_BONEMATRIX3 FLAG_BIT64(_DIRTY_BONEMATRIX3) +#define DIRTY_BONEMATRIX4 FLAG_BIT64(_DIRTY_BONEMATRIX4) +#define DIRTY_BONEMATRIX5 FLAG_BIT64(_DIRTY_BONEMATRIX5) +#define DIRTY_BONEMATRIX6 FLAG_BIT64(_DIRTY_BONEMATRIX6) +#define DIRTY_BONEMATRIX7 FLAG_BIT64(_DIRTY_BONEMATRIX7) +#define DIRTY_BEZIERCOUNTU FLAG_BIT64(_DIRTY_BEZIERCOUNTU) +#define DIRTY_SPLINECOUNTU FLAG_BIT64(_DIRTY_SPLINECOUNTU) +#define DIRTY_SPLINECOUNTV FLAG_BIT64(_DIRTY_SPLINECOUNTV) +#define DIRTY_SPLINETYPEU FLAG_BIT64(_DIRTY_SPLINETYPEU) +#define DIRTY_SPLINETYPEV FLAG_BIT64(_DIRTY_SPLINETYPEV) + // Real public interface class Shader { @@ -191,7 +239,7 @@ public: LinkedShader *ApplyFragmentShader(ShaderID VSID, Shader *vs, u32 vertType, int prim); void DirtyShader(); - void DirtyUniform(u32 what) { + void DirtyUniform(u64 what) { globalDirty_ |= what; } void DirtyLastShader(); // disables vertex arrays @@ -229,8 +277,8 @@ private: ShaderID lastVSID_; LinkedShader *lastShader_; - u32 globalDirty_; - u32 shaderSwitchDirty_; + u64 globalDirty_; + u64 shaderSwitchDirty_; char *codeBuffer_; typedef std::map FSCache; From 5aeaa95f2c9211a1961097e1a1b20c54cb436c4c Mon Sep 17 00:00:00 2001 From: xebra Date: Mon, 9 Jan 2017 03:37:21 +0900 Subject: [PATCH 07/17] [spline/bezier]Implement hardware tessellation on OpenGL. --- GPU/Common/DrawEngineCommon.h | 1 + GPU/GLES/DrawEngineGLES.cpp | 59 +++++++- GPU/GLES/DrawEngineGLES.h | 14 ++ GPU/GLES/GPU_GLES.cpp | 150 ++++++++++++++++++- GPU/GLES/ShaderManager.cpp | 58 +++++++- GPU/GLES/ShaderManager.h | 8 ++ GPU/GLES/VertexShaderGenerator.cpp | 223 +++++++++++++++++++++++++++-- 7 files changed, 498 insertions(+), 15 deletions(-) diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index 0e7d89457e..39c892bea4 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -96,6 +96,7 @@ protected: int prevSizeTex; int prevSizeCol; public: + // Send spline/bezier's control points to vertex shader through floating point texture. virtual void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) = 0; }; TessellationDataTransfer *tessDataTransfer; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 51a6c34fb1..ff4908eeb2 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -143,6 +143,8 @@ DrawEngineGLES::DrawEngineGLES() InitDeviceObjects(); register_gl_resource_holder(this); + + tessDataTransfer = new TessellationDataTransferGLES(); } DrawEngineGLES::~DrawEngineGLES() { @@ -154,6 +156,8 @@ DrawEngineGLES::~DrawEngineGLES() { FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE); unregister_gl_resource_holder(this); + + delete tessDataTransfer; } void DrawEngineGLES::RestoreVAO() { @@ -844,7 +848,11 @@ rotateVBO: SetupDecFmtForDraw(program, dec_->GetDecVtxFmt(), vbo ? 0 : decoded); if (useElements) { - glDrawElements(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex); + if (gstate_c.bezier || gstate_c.spline) + // Instanced rendering for instanced tessellation + glDrawElementsInstanced(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex, numPatches); + else + glDrawElements(glprim[prim], vertexCount, GL_UNSIGNED_SHORT, ebo ? 0 : (GLvoid*)decIndex); } else { glDrawArrays(glprim[prim], 0, vertexCount); } @@ -1105,3 +1113,52 @@ void DrawEngineGLES::DecimateBuffers() { bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const { return decJitCache_->IsInSpace(ptr); } + +void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) { + // Position + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_1D, data_tex[0]); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + if (prevSize < size) { + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB32F, size, 0, GL_RGB, GL_FLOAT, (GLfloat*)pos); + prevSize = size; + } else { + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGB, GL_FLOAT, (GLfloat*)pos); + } + + // Texcoords + if (hasTexCoords) { + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_1D, data_tex[1]); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + if (prevSizeTex < size) { + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB32F, size, 0, GL_RGB, GL_FLOAT, (GLfloat*)tex); + prevSizeTex = size; + } else { + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGB, GL_FLOAT, (GLfloat*)tex); + } + } + + // Color + glActiveTexture(GL_TEXTURE5); + glBindTexture(GL_TEXTURE_1D, data_tex[2]); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + int sizeColor = hasColor ? size : 1; + if (prevSizeCol < sizeColor) { + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, sizeColor, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col); + prevSizeCol = sizeColor; + } else { + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeColor, GL_RGBA, GL_FLOAT, (GLfloat*)col); + } + + glActiveTexture(GL_TEXTURE0); +} diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 78546187fe..afa85edc3e 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -272,4 +272,18 @@ private: bool fboTexNeedBind_; bool fboTexBound_; + + // Hardware tessellation + class TessellationDataTransferGLES : public TessellationDataTransfer { + private: + int data_tex[3]; + public: + TessellationDataTransferGLES() : TessellationDataTransfer(), data_tex() { + glGenTextures(3, (GLuint*)data_tex); + } + ~TessellationDataTransferGLES() { + glDeleteTextures(3, (GLuint*)data_tex); + } + void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; + }; }; diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 06660fae55..e2a3fa1976 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -406,7 +406,6 @@ GPU_GLES::GPU_GLES(GraphicsContext *ctx) framebufferManager_ = framebufferManagerGL_; textureCacheGL_ = new TextureCache(); textureCache_ = textureCacheGL_; - drawEngineCommon_ = &drawEngine_; drawEngine_.SetShaderManager(shaderManager_); drawEngine_.SetTextureCache(textureCacheGL_); @@ -986,6 +985,155 @@ void GPU_GLES::Execute_VertexTypeSkinning(u32 op, u32 diff) { } } +void GPU_GLES::Execute_Bezier(u32 op, u32 diff) { + // This also make skipping drawing very effective. + framebufferManagerGL_->SetRenderFrameBuffer(gstate_c.framebufChanged, gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + // TODO: Should this eat some cycles? Probably yes. Not sure if important. + return; + } + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + return; + } + + void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); + void *indices = NULL; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + return; + } + indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); + } + + if (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) { + DEBUG_LOG_REPORT(G3D, "Bezier + morph: %i", (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT); + } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Bezier + skinning: %i", vertTypeGetNumBoneWeights(gstate.vertType)); + } + + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + int bz_ucount = op & 0xFF; + 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.bezier_count_u != bz_ucount) { + shaderManager_->DirtyUniform(DIRTY_BEZIERCOUNTU); + gstate_c.bezier_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); +} + +void GPU_GLES::Execute_Spline(u32 op, u32 diff) { + // This also make skipping drawing very effective. + framebufferManagerGL_->SetRenderFrameBuffer(gstate_c.framebufChanged, gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + // TODO: Should this eat some cycles? Probably yes. Not sure if important. + return; + } + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + return; + } + + void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); + void *indices = NULL; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + return; + } + indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); + } + + if (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) { + DEBUG_LOG_REPORT(G3D, "Spline + morph: %i", (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT); + } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Spline + skinning: %i", vertTypeGetNumBoneWeights(gstate.vertType)); + } + + int sp_ucount = op & 0xFF; + int sp_vcount = (op >> 8) & 0xFF; + int sp_utype = (op >> 16) & 0x3; + int sp_vtype = (op >> 18) & 0x3; + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + 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) { + shaderManager_->DirtyUniform(DIRTY_SPLINECOUNTU); + gstate_c.spline_count_u = sp_ucount; + } + if (gstate_c.spline_count_v != sp_vcount) { + shaderManager_->DirtyUniform(DIRTY_SPLINECOUNTV); + gstate_c.spline_count_v = sp_vcount; + } + if (gstate_c.spline_type_u != sp_utype) { + shaderManager_->DirtyUniform(DIRTY_SPLINETYPEU); + gstate_c.spline_type_u = sp_utype; + } + if (gstate_c.spline_type_v != sp_vtype) { + shaderManager_->DirtyUniform(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); +} + +void GPU_GLES::Execute_BoundingBox(u32 op, u32 diff) { + // Just resetting, nothing to bound. + const u32 data = op & 0x00FFFFFF; + if (data == 0) { + // TODO: Should this set the bboxResult? Let's set it true for now. + currentList->bboxResult = true; + return; + } + if (((data & 7) == 0) && data <= 64) { // Sanity check + void *control_points = Memory::GetPointer(gstate_c.vertexAddr); + if (gstate.vertType & GE_VTYPE_IDX_MASK) { + ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Indexed bounding box data not supported."); + // Data seems invalid. Let's assume the box test passed. + currentList->bboxResult = true; + return; + } + + // Test if the bounding box is within the drawing region. + currentList->bboxResult = drawEngine_.TestBoundingBox(control_points, data, gstate.vertType); + } else { + ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Bad bounding box data: %06x", data); + // Data seems invalid. Let's assume the box test passed. + currentList->bboxResult = true; + } +} + void GPU_GLES::Execute_Region(u32 op, u32 diff) { gstate_c.framebufChanged = true; gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 0033b5c78e..30f6da6728 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -229,6 +229,17 @@ LinkedShader::LinkedShader(ShaderID VSID, Shader *vs, ShaderID FSID, Shader *fs, sprintf(temp, "u_lightspecular%i", i); u_lightspecular[i] = glGetUniformLocation(program, temp); } + if (gstate_c.bezier || gstate_c.spline) { + u_tess_pos_tex = glGetUniformLocation(program, "u_tess_pos_tex"); + u_tess_tex_tex = glGetUniformLocation(program, "u_tess_tex_tex"); + u_tess_col_tex = glGetUniformLocation(program, "u_tess_col_tex"); + u_spline_count_u = glGetUniformLocation(program, "u_spline_count_u"); + if (gstate_c.spline) { + u_spline_count_v = glGetUniformLocation(program, "u_spline_count_v"); + u_spline_type_u = glGetUniformLocation(program, "u_spline_type_u"); + u_spline_type_v = glGetUniformLocation(program, "u_spline_type_v"); + } + } attrMask = 0; if (-1 != glGetAttribLocation(program, "position")) attrMask |= 1 << ATTR_POSITION; @@ -282,6 +293,14 @@ LinkedShader::LinkedShader(ShaderID VSID, Shader *vs, ShaderID FSID, Shader *fs, u_lightpos[i] != -1) availableUniforms |= DIRTY_LIGHT0 << i; } + if (gstate_c.bezier) { + if (u_spline_count_u != -1) availableUniforms |= DIRTY_BEZIERCOUNTU; + } else if (gstate_c.spline) { + if (u_spline_count_u != -1) availableUniforms |= DIRTY_SPLINECOUNTU; + if (u_spline_count_v != -1) availableUniforms |= DIRTY_SPLINECOUNTV; + if (u_spline_type_u != -1) availableUniforms |= DIRTY_SPLINETYPEU; + if (u_spline_type_v != -1) availableUniforms |= DIRTY_SPLINETYPEV; + } glUseProgram(program); @@ -289,6 +308,11 @@ LinkedShader::LinkedShader(ShaderID VSID, Shader *vs, ShaderID FSID, Shader *fs, glUniform1i(u_tex, 0); glUniform1i(u_fbotex, 1); glUniform1i(u_testtex, 2); + if (gstate_c.bezier || gstate_c.spline) { + glUniform1i(u_tess_pos_tex, 3); // Texture unit 3 + glUniform1i(u_tess_tex_tex, 4); // Texture unit 4 + glUniform1i(u_tess_col_tex, 5); // Texture unit 5 + } // The rest, use the "dirty" mechanism. dirtyUniforms = DIRTY_ALL; } @@ -537,10 +561,22 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { const float widthFactor = (float)w * invW; const float heightFactor = (float)h * invH; float uvscaleoff[4]; - uvscaleoff[0] = widthFactor; - uvscaleoff[1] = heightFactor; - uvscaleoff[2] = 0.0f; - uvscaleoff[3] = 0.0f; + if (gstate_c.bezier || gstate_c.spline) { + // TODO: Move somewhere or fix properly + // Fix temporarily avoid texture scaling bug with hardware tessellation. + // This issue occurs probably since rev 9d7983e. + static const float rescale[4] = {1.0f, 2*127.5f/128.f, 2*32767.5f/32768.f, 1.0f}; + const float factor = rescale[(vertType & GE_VTYPE_TC_MASK) >> GE_VTYPE_TC_SHIFT]; + uvscaleoff[0] = gstate_c.uv.uScale * factor * widthFactor; + uvscaleoff[1] = gstate_c.uv.vScale * factor * heightFactor; + uvscaleoff[2] = gstate_c.uv.uOff * widthFactor; + uvscaleoff[3] = gstate_c.uv.vOff * heightFactor; + } else { + uvscaleoff[0] = widthFactor; + uvscaleoff[1] = heightFactor; + uvscaleoff[2] = 0.0f; + uvscaleoff[3] = 0.0f; + } glUniform4fv(u_uvscaleoffset, 1, uvscaleoff); } @@ -704,6 +740,20 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { if (u_lightspecular[i] != -1) SetColorUniform3(u_lightspecular[i], gstate.lcolor[i * 3 + 2]); } } + + if (gstate_c.bezier) { + if (dirty & DIRTY_BEZIERCOUNTU) + glUniform1i(u_spline_count_u, gstate_c.bezier_count_u); + } else if (gstate_c.spline) { + if (dirty & DIRTY_SPLINECOUNTU) + glUniform1i(u_spline_count_u, gstate_c.spline_count_u); + if (dirty & DIRTY_SPLINECOUNTV) + glUniform1i(u_spline_count_v, gstate_c.spline_count_v); + if (dirty & DIRTY_SPLINETYPEU) + glUniform1i(u_spline_type_u, gstate_c.spline_type_u); + if (dirty & DIRTY_SPLINETYPEV) + glUniform1i(u_spline_type_v, gstate_c.spline_type_v); + } } ShaderManager::ShaderManager() diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index 0cc8ca5b99..2ffa930a12 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -110,6 +110,14 @@ public: int u_lightdiffuse[4]; // each light consist of vec4[3] int u_lightspecular[4]; // attenuation int u_lightambient[4]; // attenuation + + int u_tess_pos_tex; + int u_tess_tex_tex; + int u_tess_col_tex; + int u_spline_count_u; + int u_spline_count_v; + int u_spline_type_u; + int u_spline_type_v; }; enum { diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index ed17abd76f..7d85d3900d 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -170,6 +170,12 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { 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); + const char *shading = ""; if (glslES30) shading = doFlatShading ? "flat " : ""; @@ -319,6 +325,87 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { WRITE(p, "}\n\n"); } + // Hardware tessellation + if (doBezier || doSpline) { + WRITE(p, "uniform sampler1D u_tess_pos_tex;\n"); + WRITE(p, "uniform sampler1D u_tess_tex_tex;\n"); + WRITE(p, "uniform sampler1D u_tess_col_tex;\n"); + + WRITE(p, "uniform int u_spline_count_u;\n"); + + for (int i = 2; i <= 4; i++) { + // Define 3 types vec2, vec3, vec4 + WRITE(p, "vec%d tess_sample(in vec%d points[16], in vec2 weights[4]) {\n", i, i); + WRITE(p, " vec%d pos = vec%d(0);\n", i, i); + 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)\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, "uniform int u_spline_count_v;\n"); + WRITE(p, "uniform int u_spline_type_u;\n"); + WRITE(p, "uniform int u_spline_type_v;\n"); + + WRITE(p, "void spline_knot(ivec2 num_patches, ivec2 type, out vec2 knot[6], ivec2 patch_pos) {\n"); + WRITE(p, " for (int i = 0; i < 6; ++i) {\n"); + WRITE(p, " knot[i] = vec2(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 (patch_pos.x <= 2)\n"); + WRITE(p, " knot[0].x = 0;\n"); + WRITE(p, " if (patch_pos.x <= 1)\n"); + WRITE(p, " knot[1].x = 0;\n"); + WRITE(p, " }\n"); + WRITE(p, " if ((type.x & 2) != 0) {\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 (patch_pos.y <= 2)\n"); + WRITE(p, " knot[0].y = 0;\n"); + WRITE(p, " if (patch_pos.y <= 1)\n"); + WRITE(p, " knot[1].y = 0;\n"); + WRITE(p, " }\n"); + WRITE(p, " if ((type.y & 2) != 0) {\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(vec2 t, in vec2 knot[6], out vec2 weights[4]) {\n"); + // TODO: Maybe compilers could be coaxed into vectorizing this code without the above explicitly... + WRITE(p, " vec2 t0 = (t - knot[0]);\n"); + WRITE(p, " vec2 t1 = (t - knot[1]);\n"); + WRITE(p, " vec2 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, " vec2 f30 = t0 / (knot[3] - knot[0]);\n"); + WRITE(p, " vec2 f41 = t1 / (knot[4] - knot[1]);\n"); + WRITE(p, " vec2 f52 = t2 / (knot[5] - knot[2]);\n"); + WRITE(p, " vec2 f31 = t1 / (knot[3] - knot[1]);\n"); + WRITE(p, " vec2 f42 = t2 / (knot[4] - knot[2]);\n"); + WRITE(p, " vec2 f32 = t2 / (knot[3] - knot[2]);\n"); + WRITE(p, " vec2 a = (1 - f30)*(1 - f31);\n"); + WRITE(p, " vec2 b = (f31*f41);\n"); + WRITE(p, " vec2 c = (1 - f41)*(1 - f42);\n"); + WRITE(p, " vec2 d = (f42*f52);\n"); + WRITE(p, " weights[0] = a - (a*f32);\n"); + WRITE(p, " weights[1] = 1 - a - b + ((a + b + c - 1)*f32);\n"); + WRITE(p, " weights[2] = b + ((1 - b - c - d)*f32);\n"); + WRITE(p, " weights[3] = d*f32;\n"); + WRITE(p, "}\n"); + } + } + WRITE(p, "void main() {\n"); if (!useHWTransform) { @@ -355,12 +442,116 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { } else { // Step 1: World Transform / Skinning if (!enableBones) { - // No skinning, just standard T&L. - WRITE(p, " vec3 worldpos = (u_world * vec4(position.xyz, 1.0)).xyz;\n"); - if (hasNormal) - WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snormal, 0.0)).xyz);\n", flipNormal ? "-" : ""); - else - WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); + // Hardware tessellation + if (doBezier || doSpline) { + WRITE(p, " vec3 _pos[16];\n"); + WRITE(p, " vec2 _tex[16];\n"); + WRITE(p, " vec4 _col[16];\n"); + WRITE(p, " int num_patches_u = %s;\n", doBezier ? "(u_spline_count_u - 1) / 3" : "u_spline_count_u - 3"); + WRITE(p, " int u = int(mod(gl_InstanceID, num_patches_u));\n"); + WRITE(p, " int v = (gl_InstanceID / num_patches_u);\n"); + WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n"); + WRITE(p, " for (int i = 0; i < 4; i++) {\n"); + WRITE(p, " for (int j = 0; j < 4; j++) {\n"); + WRITE(p, " int index = (i + v%s) * u_spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); + WRITE(p, " _pos[i * 4 + j] = texelFetch(u_tess_pos_tex, index, 0).xyz;\n"); + if (doTexture && hasTexcoord && hasTexcoordTess) + WRITE(p, " _tex[i * 4 + j] = texelFetch(u_tess_tex_tex, index, 0).xy;\n"); + if (hasColor && hasColorTess) + WRITE(p, " _col[i * 4 + j] = texelFetch(u_tess_col_tex, index, 0).rgba;\n"); + WRITE(p, " }\n"); + WRITE(p, " }\n"); + WRITE(p, " vec2 tess_pos = position.xy;\n"); + WRITE(p, " vec2 weights[4];\n"); + if (doBezier) { + // Bernstein 3D + WRITE(p, " weights[0] = (1 - tess_pos) * (1 - tess_pos) * (1 - tess_pos);\n"); + WRITE(p, " weights[1] = 3 * tess_pos * (1 - tess_pos) * (1 - tess_pos);\n"); + WRITE(p, " weights[2] = 3 * tess_pos * tess_pos * (1 - tess_pos);\n"); + WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n"); + } else if (doSpline) { + WRITE(p, " ivec2 spline_num_patches = ivec2(u_spline_count_u - 3, u_spline_count_v - 3);\n"); + WRITE(p, " ivec2 spline_type = ivec2(u_spline_type_u, u_spline_type_v);\n"); + WRITE(p, " vec2 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, " vec3 pos = tess_sample(_pos, weights);\n"); + if (doTexture && hasTexcoord) { + if (hasTexcoordTess) + WRITE(p, " vec2 tex = tess_sample(_tex, weights);\n"); + else + WRITE(p, " vec2 tex = tess_pos + patch_pos;\n"); + } + if (hasColor) { + if (hasColorTess) + WRITE(p, " vec4 col = tess_sample(_col, weights);\n"); + else + WRITE(p, " vec4 col = texelFetch(u_tess_col_tex, 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, " vec2 bernderiv[4];\n"); + WRITE(p, " bernderiv[0] = -3 * (tess_pos - 1) * (tess_pos - 1); \n"); + WRITE(p, " bernderiv[1] = 9 * tess_pos * tess_pos - 12 * tess_pos + 3; \n"); + WRITE(p, " bernderiv[2] = 3 * (2 - 3 * tess_pos) * tess_pos; \n"); + WRITE(p, " bernderiv[3] = 3 * tess_pos * tess_pos; \n"); + + WRITE(p, " vec2 bernderiv_u[4];\n"); + WRITE(p, " vec2 bernderiv_v[4];\n"); + WRITE(p, " for (int i = 0; i < 4; i++) {\n"); + WRITE(p, " bernderiv_u[i] = vec2(bernderiv[i].x, weights[i].y);\n"); + WRITE(p, " bernderiv_v[i] = vec2(weights[i].x, bernderiv[i].y);\n"); + WRITE(p, " }\n"); + + WRITE(p, " vec3 du = tess_sample(_pos, bernderiv_u);\n"); + WRITE(p, " vec3 dv = tess_sample(_pos, bernderiv_v);\n"); + } else if (doSpline) { + WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0);\n"); + WRITE(p, " vec2 tess_next_v = vec2(0, normal.y);\n"); + // Right + WRITE(p, " vec2 tess_pos_r = tess_pos + tess_next_u;\n"); + WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); + WRITE(p, " spline_weight(tess_pos_r + patch_pos, knots, weights);\n"); + WRITE(p, " vec3 pos_r = tess_sample(_pos, weights);\n"); + // Left + WRITE(p, " vec2 tess_pos_l = tess_pos - tess_next_u;\n"); + WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); + WRITE(p, " spline_weight(tess_pos_l + patch_pos, knots, weights);\n"); + WRITE(p, " vec3 pos_l = tess_sample(_pos, weights);\n"); + // Down + WRITE(p, " vec2 tess_pos_d = tess_pos + tess_next_v;\n"); + WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); + WRITE(p, " spline_weight(tess_pos_d + patch_pos, knots, weights);\n"); + WRITE(p, " vec3 pos_d = tess_sample(_pos, weights);\n"); + // Up + WRITE(p, " vec2 tess_pos_u = tess_pos - tess_next_v;\n"); + WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n"); + WRITE(p, " spline_weight(tess_pos_u + patch_pos, knots, weights);\n"); + WRITE(p, " vec3 pos_u = tess_sample(_pos, weights);\n"); + + WRITE(p, " vec3 du = pos_r - pos_l;\n"); + WRITE(p, " vec3 dv = pos_d - pos_u;\n"); + } + WRITE(p, " vec3 nrm = cross(du, dv);\n"); + WRITE(p, " nrm = normalize(nrm);\n"); + } + WRITE(p, " vec3 worldpos = (u_world * vec4(pos.xyz, 1.0)).xyz;\n"); + if (hasNormal) { + WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snrm, 0.0)).xyz);\n", flipNormalTess ? "-" : ""); + } else { + WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); + } + } else { + // No skinning, just standard T&L. + WRITE(p, " vec3 worldpos = (u_world * vec4(position.xyz, 1.0)).xyz;\n"); + if (hasNormal) + WRITE(p, " mediump vec3 worldnormal = normalize((u_world * vec4(%snormal, 0.0)).xyz);\n", flipNormal ? "-" : ""); + else + WRITE(p, " mediump vec3 worldnormal = vec3(0.0, 0.0, 1.0);\n"); + } } else { static const char *rescale[4] = {"", " * 1.9921875", " * 1.999969482421875", ""}; // 2*127.5f/128.f, 2*32767.5f/32768.f, 1.0f}; const char *factor = rescale[boneWeightScale]; @@ -452,6 +643,11 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { const char *ambientStr = (matUpdate & 1) && hasColor ? "color0" : "u_matambientalpha"; const char *diffuseStr = (matUpdate & 2) && hasColor ? "color0.rgb" : "u_matdiffuse"; const char *specularStr = (matUpdate & 4) && hasColor ? "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; @@ -570,7 +766,10 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { } else { // Lighting doesn't affect color. if (hasColor) { - WRITE(p, " v_color0 = color0;\n"); + if (doBezier || doSpline) + WRITE(p, " v_color0 = col;\n"); + else + WRITE(p, " v_color0 = color0;\n"); } else { WRITE(p, " v_color0 = u_matambientalpha;\n"); } @@ -585,13 +784,19 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { case GE_TEXMAP_UNKNOWN: // Not sure what this is, but Riviera uses it. Treating as coords works. if (scaleUV) { if (hasTexcoord) { - WRITE(p, " v_texcoord = texcoord * u_uvscaleoffset.xy;\n"); + if (doBezier || doSpline) + WRITE(p, " v_texcoord = tex * u_uvscaleoffset.xy;\n"); + else + WRITE(p, " v_texcoord = texcoord * u_uvscaleoffset.xy;\n"); } else { WRITE(p, " v_texcoord = vec2(0.0);\n"); } } else { if (hasTexcoord) { - WRITE(p, " v_texcoord = texcoord * u_uvscaleoffset.xy + u_uvscaleoffset.zw;\n"); + if (doBezier || doSpline) + WRITE(p, " v_texcoord = tex * u_uvscaleoffset.xy + u_uvscaleoffset.zw;\n"); + else + WRITE(p, " v_texcoord = texcoord * u_uvscaleoffset.xy + u_uvscaleoffset.zw;\n"); } else { WRITE(p, " v_texcoord = u_uvscaleoffset.zw;\n"); } From 95b534ef881fa321ca50376b63c3cea112870adc Mon Sep 17 00:00:00 2001 From: xebra Date: Mon, 9 Jan 2017 04:57:21 +0900 Subject: [PATCH 08/17] Update credit --- UI/MiscScreens.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 4cdb466fbf..c71efc5b7c 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -608,7 +608,7 @@ void CreditsScreen::render() { "Igor Calabria", "Coldbird", "Kyhel", - "", + "xebra", "", cr->T("specialthanks", "Special thanks to:"), "Maxim for his amazing Atrac3+ decoder work", From 67503ed160939b8e2d32cb9a5f45ed172e59054f Mon Sep 17 00:00:00 2001 From: xebra Date: Mon, 9 Jan 2017 18:55:22 +0900 Subject: [PATCH 09/17] [spline/bezier]Small fixes for compatibility with GLES 2.0. --- GPU/GLES/DrawEngineGLES.cpp | 42 +++++++++++++++--------------- GPU/GLES/VertexShaderGenerator.cpp | 11 ++++---- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index ff4908eeb2..1670903c33 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -1117,47 +1117,47 @@ bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const { void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) { // Position glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_1D, data_tex[0]); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glBindTexture(GL_TEXTURE_2D, data_tex[0]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (prevSize < size) { - glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB32F, size, 0, GL_RGB, GL_FLOAT, (GLfloat*)pos); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, size, 1, 0, GL_RGB, GL_FLOAT, (GLfloat*)pos); prevSize = size; } else { - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGB, GL_FLOAT, (GLfloat*)pos); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, 1, GL_RGB, GL_FLOAT, (GLfloat*)pos); } // Texcoords if (hasTexCoords) { glActiveTexture(GL_TEXTURE4); - glBindTexture(GL_TEXTURE_1D, data_tex[1]); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glBindTexture(GL_TEXTURE_2D, data_tex[1]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (prevSizeTex < size) { - glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB32F, size, 0, GL_RGB, GL_FLOAT, (GLfloat*)tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, size, 1, 0, GL_RGB, GL_FLOAT, (GLfloat*)tex); prevSizeTex = size; } else { - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGB, GL_FLOAT, (GLfloat*)tex); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, 1, GL_RGB, GL_FLOAT, (GLfloat*)tex); } } // Color glActiveTexture(GL_TEXTURE5); - glBindTexture(GL_TEXTURE_1D, data_tex[2]); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glBindTexture(GL_TEXTURE_2D, data_tex[2]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); int sizeColor = hasColor ? size : 1; if (prevSizeCol < sizeColor) { - glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, sizeColor, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, sizeColor, 1, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col); prevSizeCol = sizeColor; } else { - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeColor, GL_RGBA, GL_FLOAT, (GLfloat*)col); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeColor, 1, GL_RGBA, GL_FLOAT, (GLfloat*)col); } glActiveTexture(GL_TEXTURE0); diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index 7d85d3900d..772882ccee 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -327,9 +327,9 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { // Hardware tessellation if (doBezier || doSpline) { - WRITE(p, "uniform sampler1D u_tess_pos_tex;\n"); - WRITE(p, "uniform sampler1D u_tess_tex_tex;\n"); - WRITE(p, "uniform sampler1D u_tess_col_tex;\n"); + WRITE(p, "uniform sampler2D u_tess_pos_tex;\n"); + WRITE(p, "uniform sampler2D u_tess_tex_tex;\n"); + WRITE(p, "uniform sampler2D u_tess_col_tex;\n"); WRITE(p, "uniform int u_spline_count_u;\n"); @@ -453,7 +453,8 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n"); WRITE(p, " for (int i = 0; i < 4; i++) {\n"); WRITE(p, " for (int j = 0; j < 4; j++) {\n"); - WRITE(p, " int index = (i + v%s) * u_spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); + WRITE(p, " int idx = (i + v%s) * u_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"); if (doTexture && hasTexcoord && hasTexcoordTess) WRITE(p, " _tex[i * 4 + j] = texelFetch(u_tess_tex_tex, index, 0).xy;\n"); @@ -487,7 +488,7 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { if (hasColorTess) WRITE(p, " vec4 col = tess_sample(_col, weights);\n"); else - WRITE(p, " vec4 col = texelFetch(u_tess_col_tex, 0, 0).rgba;\n"); + WRITE(p, " vec4 col = texelFetch(u_tess_col_tex, ivec2(0, 0), 0).rgba;\n"); } if (hasNormal) { // Curved surface is probably always need to compute normal(not sampling from control points) From 2e7f2998c7ed84e5a275079d47d60adbfd7a5d41 Mon Sep 17 00:00:00 2001 From: xebra Date: Mon, 9 Jan 2017 19:18:27 +0900 Subject: [PATCH 10/17] Exclusion control for options between spline/bezier quality and hardware tessellation. --- UI/GameSettingsScreen.cpp | 8 +++++++- UI/GameSettingsScreen.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 0de0fbada4..c1f681f61c 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -278,9 +278,14 @@ void GameSettingsScreen::CreateViews() { } return UI::EVENT_CONTINUE; }); - beziersChoice->SetDisabledPtr(&g_Config.bSoftwareRendering); + bezierChoiceDisable_ = g_Config.bSoftwareRendering || g_Config.bHardwareTessellation; + beziersChoice->SetDisabledPtr(&bezierChoiceDisable_); CheckBox *tessellationHW = graphicsSettings->Add(new CheckBox(&g_Config.bHardwareTessellation, gr->T("Hardware Tessellation"))); + tessellationHW->OnClick.Add([=](EventParams &e) { + bezierChoiceDisable_ = g_Config.bSoftwareRendering || g_Config.bHardwareTessellation; + return UI::EVENT_CONTINUE; + }); tessellationHW->SetEnabledPtr(&vtxCacheEnable_); // Same as Vertex Cache(!g_Config.bSoftwareRendering && g_Config.bHardwareTransform) // In case we're going to add few other antialiasing option like MSAA in the future. @@ -718,6 +723,7 @@ UI::EventReturn GameSettingsScreen::OnSoftwareRendering(UI::EventParams &e) { vtxCacheEnable_ = !g_Config.bSoftwareRendering && g_Config.bHardwareTransform; 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; return UI::EVENT_DONE; } diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 64b15e20dc..64ff85a100 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -116,6 +116,7 @@ private: bool postProcEnable_; bool resolutionEnable_; bool bloomHackEnable_; + bool bezierChoiceDisable_; }; class SettingInfoMessage : public UI::TextView { From 334afbd1aefe80ab896ef65ef2743cf6e11d6eeb Mon Sep 17 00:00:00 2001 From: xebra Date: Tue, 10 Jan 2017 14:16:03 +0900 Subject: [PATCH 11/17] [spline/bezier]Fix again about compatibility with GLES. from @LunaMoo. --- GPU/GLES/DrawEngineGLES.cpp | 48 ++++++++++++++++-------------- GPU/GLES/VertexShaderGenerator.cpp | 19 ++++++------ 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 1670903c33..e2a14d31ba 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -1113,51 +1113,55 @@ void DrawEngineGLES::DecimateBuffers() { bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const { return decJitCache_->IsInSpace(ptr); } - void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) { +#ifdef USING_GLES2 +#define GL_TEXTURE_1D GL_TEXTURE_2D +#define glTexImage1D(t, l, ifmt, w, br, fmt, type, p) glTexImage2D(t, l, ifmt, w, 1, br, fmt, type, p) +#define glTexSubImage1D(t, l, xo, w, fmt, type, p) glTexSubImage2D(t, l, xo, 0, w, 1, fmt, type, p) +#endif // Position glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, data_tex[0]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_1D, data_tex[0]); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (prevSize < size) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, size, 1, 0, GL_RGB, GL_FLOAT, (GLfloat*)pos); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB32F, size, 0, GL_RGB, GL_FLOAT, (GLfloat*)pos); prevSize = size; } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, 1, GL_RGB, GL_FLOAT, (GLfloat*)pos); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGB, GL_FLOAT, (GLfloat*)pos); } // Texcoords if (hasTexCoords) { glActiveTexture(GL_TEXTURE4); - glBindTexture(GL_TEXTURE_2D, data_tex[1]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_1D, data_tex[1]); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (prevSizeTex < size) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, size, 1, 0, GL_RGB, GL_FLOAT, (GLfloat*)tex); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB32F, size, 0, GL_RGB, GL_FLOAT, (GLfloat*)tex); prevSizeTex = size; } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, 1, GL_RGB, GL_FLOAT, (GLfloat*)tex); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, size, GL_RGB, GL_FLOAT, (GLfloat*)tex); } } // Color glActiveTexture(GL_TEXTURE5); - glBindTexture(GL_TEXTURE_2D, data_tex[2]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_1D, data_tex[2]); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); int sizeColor = hasColor ? size : 1; if (prevSizeCol < sizeColor) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, sizeColor, 1, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, sizeColor, 0, GL_RGBA, GL_FLOAT, (GLfloat*)col); prevSizeCol = sizeColor; } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sizeColor, 1, GL_RGBA, GL_FLOAT, (GLfloat*)col); + glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeColor, GL_RGBA, GL_FLOAT, (GLfloat*)col); } glActiveTexture(GL_TEXTURE0); diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index 772882ccee..45189a9d92 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -327,9 +327,10 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { // Hardware tessellation if (doBezier || doSpline) { - WRITE(p, "uniform sampler2D u_tess_pos_tex;\n"); - WRITE(p, "uniform sampler2D u_tess_tex_tex;\n"); - WRITE(p, "uniform sampler2D u_tess_col_tex;\n"); + const char *sampler = gl_extensions.IsGLES ? "sampler2D" : "sampler1D"; + WRITE(p, "uniform %s u_tess_pos_tex;\n", sampler); + WRITE(p, "uniform %s u_tess_tex_tex;\n", sampler); + WRITE(p, "uniform %s u_tess_col_tex;\n", sampler); WRITE(p, "uniform int u_spline_count_u;\n"); @@ -453,13 +454,13 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { WRITE(p, " ivec2 patch_pos = ivec2(u, v);\n"); 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) * u_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, " int index = (i + v%s) * u_spline_count_u + (j + u%s);\n", doBezier ? " * 3" : "", doBezier ? " * 3" : ""); + const char *index = gl_extensions.IsGLES ? "ivec2(index, 0)" : "index"; + WRITE(p, " _pos[i * 4 + j] = texelFetch(u_tess_pos_tex, %s, 0).xyz;\n", index); 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] = texelFetch(u_tess_tex_tex, %s, 0).xy;\n", index); if (hasColor && hasColorTess) - WRITE(p, " _col[i * 4 + j] = texelFetch(u_tess_col_tex, index, 0).rgba;\n"); + WRITE(p, " _col[i * 4 + j] = texelFetch(u_tess_col_tex, %s, 0).rgba;\n", index); WRITE(p, " }\n"); WRITE(p, " }\n"); WRITE(p, " vec2 tess_pos = position.xy;\n"); @@ -488,7 +489,7 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { 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 = texelFetch(u_tess_col_tex, %s, 0).rgba;\n", gl_extensions.IsGLES ? "ivec2(0, 0)" : "0"); } if (hasNormal) { // Curved surface is probably always need to compute normal(not sampling from control points) From deea2583832c796bcdea7ef5390ba30ce5c43923 Mon Sep 17 00:00:00 2001 From: xebra Date: Tue, 10 Jan 2017 14:41:01 +0900 Subject: [PATCH 12/17] [spline/bezier]Fix to avoid crashing when activated hardware tessellation with dx9 and vulkan. --- GPU/Directx9/DrawEngineDX9.cpp | 8 ++++++++ GPU/Directx9/DrawEngineDX9.h | 12 ++++++++++++ GPU/GLES/DrawEngineGLES.cpp | 1 + GPU/Vulkan/DrawEngineVulkan.cpp | 8 ++++++++ GPU/Vulkan/DrawEngineVulkan.h | 12 ++++++++++++ 5 files changed, 41 insertions(+) diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 9609380e67..493c6f2ad8 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -107,6 +107,8 @@ DrawEngineDX9::DrawEngineDX9() indexGen.Setup(decIndex); InitDeviceObjects(); + + tessDataTransfer = new TessellationDataTransferDX9(); } DrawEngineDX9::~DrawEngineDX9() { @@ -121,6 +123,8 @@ DrawEngineDX9::~DrawEngineDX9() { decl->second->Release(); } } + + delete tessDataTransfer; } void DrawEngineDX9::InitDeviceObjects() { @@ -902,4 +906,8 @@ bool DrawEngineDX9::IsCodePtrVertexDecoder(const u8 *ptr) const { return decJitCache_->IsInSpace(ptr); } +void DX9::DrawEngineDX9::TessellationDataTransferDX9::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) +{ +} + } // namespace diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index 608c3e8efd..d9513aa785 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -246,6 +246,18 @@ private: bool fboTexNeedBind_; bool fboTexBound_; + + // Hardware tessellation + class TessellationDataTransferDX9 : public TessellationDataTransfer { + private: + int data_tex[3]; + public: + TessellationDataTransferDX9() : TessellationDataTransfer(), data_tex() { + } + ~TessellationDataTransferDX9() { + } + void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; + }; }; } // namespace diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index e2a14d31ba..8a50df18e4 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -1113,6 +1113,7 @@ void DrawEngineGLES::DecimateBuffers() { bool DrawEngineGLES::IsCodePtrVertexDecoder(const u8 *ptr) const { return decJitCache_->IsInSpace(ptr); } + void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) { #ifdef USING_GLES2 #define GL_TEXTURE_1D GL_TEXTURE_2D diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 98feecbf37..308ba38a95 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -92,6 +92,8 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan) indexGen.Setup(decIndex); InitDeviceObjects(); + + tessDataTransfer = new TessellationDataTransferVulkan(); } void DrawEngineVulkan::InitDeviceObjects() { @@ -194,6 +196,8 @@ DrawEngineVulkan::~DrawEngineVulkan() { FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE); DestroyDeviceObjects(); + + delete tessDataTransfer; } void DrawEngineVulkan::FrameData::Destroy(VulkanContext *vulkan) { @@ -901,3 +905,7 @@ void DrawEngineVulkan::Resized() { bool DrawEngineVulkan::IsCodePtrVertexDecoder(const u8 *ptr) const { return decJitCache_->IsInSpace(ptr); } + +void DrawEngineVulkan::TessellationDataTransferVulkan::SendDataToShader(const float * pos, const float * tex, const float * col, int size, bool hasColor, bool hasTexCoords) +{ +} diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 2f1967a26b..c27a5a8056 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -271,4 +271,16 @@ private: bool fboTexBound_; DrawEngineVulkanStats stats_; + + // Hardware tessellation + class TessellationDataTransferVulkan : public TessellationDataTransfer { + private: + int data_tex[3]; + public: + TessellationDataTransferVulkan() : TessellationDataTransfer(), data_tex() { + } + ~TessellationDataTransferVulkan() { + } + void SendDataToShader(const float *pos, const float *tex, const float *col, int size, bool hasColor, bool hasTexCoords) override; + }; }; From de253a6aef013b728a1403ef1ecfac4978eb1e6a Mon Sep 17 00:00:00 2001 From: xebra Date: Sun, 22 Jan 2017 23:44:27 +0900 Subject: [PATCH 13/17] [spline/bezier]Revert #ab9c1d4 about spline/bezier. --- GPU/Directx9/GPU_DX9.cpp | 89 +++++++++++++++++++++++++++++++++++++++ GPU/Directx9/GPU_DX9.h | 2 + GPU/GLES/GPU_GLES.cpp | 26 ------------ GPU/GLES/GPU_GLES.h | 2 + GPU/Vulkan/GPU_Vulkan.cpp | 89 +++++++++++++++++++++++++++++++++++++++ GPU/Vulkan/GPU_Vulkan.h | 2 + 6 files changed, 184 insertions(+), 26 deletions(-) diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 629cb787f5..9af047151a 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -797,6 +797,95 @@ void GPU_DX9::Execute_Prim(u32 op, u32 diff) { AdvanceVerts(vertexType, count, bytesRead); } +void GPU_DX9::Execute_Bezier(u32 op, u32 diff) { + // This also make skipping drawing very effective. + framebufferManagerDX9_->SetRenderFrameBuffer(gstate_c.framebufChanged, gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + // TODO: Should this eat some cycles? Probably yes. Not sure if important. + return; + } + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + return; + } + + void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); + void *indices = NULL; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + return; + } + indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); + } + + if (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) { + DEBUG_LOG_REPORT(G3D, "Bezier + morph: %i", (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT); + } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Bezier + skinning: %i", vertTypeGetNumBoneWeights(gstate.vertType)); + } + + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + int bz_ucount = op & 0xFF; + int bz_vcount = (op >> 8) & 0xFF; + bool computeNormals = gstate.isLightingEnabled(); + bool patchFacing = gstate.patchfacing & 1; + int bytesRead = 0; + drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); + + // After drawing, we advance pointers - see SubmitPrim which does the same. + int count = bz_ucount * bz_vcount; + AdvanceVerts(gstate.vertType, count, bytesRead); +} + +void GPU_DX9::Execute_Spline(u32 op, u32 diff) { + // This also make skipping drawing very effective. + framebufferManagerDX9_->SetRenderFrameBuffer(gstate_c.framebufChanged, gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + // TODO: Should this eat some cycles? Probably yes. Not sure if important. + return; + } + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + return; + } + + void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); + void *indices = NULL; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + return; + } + indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); + } + + if (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) { + DEBUG_LOG_REPORT(G3D, "Spline + morph: %i", (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT); + } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Spline + skinning: %i", vertTypeGetNumBoneWeights(gstate.vertType)); + } + + int sp_ucount = op & 0xFF; + int sp_vcount = (op >> 8) & 0xFF; + int sp_utype = (op >> 16) & 0x3; + int sp_vtype = (op >> 18) & 0x3; + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + bool computeNormals = gstate.isLightingEnabled(); + bool patchFacing = gstate.patchfacing & 1; + u32 vertType = gstate.vertType; + 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); + + // After drawing, we advance pointers - see SubmitPrim which does the same. + int count = sp_ucount * sp_vcount; + AdvanceVerts(gstate.vertType, count, bytesRead); +} + void GPU_DX9::Execute_ViewportType(u32 op, u32 diff) { gstate_c.framebufChanged = true; gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index ff9704cd31..fb91c0e775 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -84,6 +84,8 @@ public: void Execute_Vaddr(u32 op, u32 diff); void Execute_Iaddr(u32 op, u32 diff); void Execute_Prim(u32 op, u32 diff); + void Execute_Bezier(u32 op, u32 diff); + void Execute_Spline(u32 op, u32 diff); void Execute_VertexType(u32 op, u32 diff); void Execute_VertexTypeSkinning(u32 op, u32 diff); void Execute_Region(u32 op, u32 diff); diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index e2a3fa1976..e81aa994ff 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -1108,32 +1108,6 @@ void GPU_GLES::Execute_Spline(u32 op, u32 diff) { AdvanceVerts(gstate.vertType, count, bytesRead); } -void GPU_GLES::Execute_BoundingBox(u32 op, u32 diff) { - // Just resetting, nothing to bound. - const u32 data = op & 0x00FFFFFF; - if (data == 0) { - // TODO: Should this set the bboxResult? Let's set it true for now. - currentList->bboxResult = true; - return; - } - if (((data & 7) == 0) && data <= 64) { // Sanity check - void *control_points = Memory::GetPointer(gstate_c.vertexAddr); - if (gstate.vertType & GE_VTYPE_IDX_MASK) { - ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Indexed bounding box data not supported."); - // Data seems invalid. Let's assume the box test passed. - currentList->bboxResult = true; - return; - } - - // Test if the bounding box is within the drawing region. - currentList->bboxResult = drawEngine_.TestBoundingBox(control_points, data, gstate.vertType); - } else { - ERROR_LOG_REPORT_ONCE(boundingbox, G3D, "Bad bounding box data: %06x", data); - // Data seems invalid. Let's assume the box test passed. - currentList->bboxResult = true; - } -} - void GPU_GLES::Execute_Region(u32 op, u32 diff) { gstate_c.framebufChanged = true; gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index 2a362a7d5e..72cff53626 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -89,6 +89,8 @@ public: void Execute_Vaddr(u32 op, u32 diff); void Execute_Iaddr(u32 op, u32 diff); void Execute_Prim(u32 op, u32 diff); + void Execute_Bezier(u32 op, u32 diff); + void Execute_Spline(u32 op, u32 diff); void Execute_VertexType(u32 op, u32 diff); void Execute_VertexTypeSkinning(u32 op, u32 diff); void Execute_Region(u32 op, u32 diff); diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index dde2855e45..f706e97045 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -828,6 +828,95 @@ void GPU_Vulkan::Execute_VertexType(u32 op, u32 diff) { } } +void GPU_Vulkan::Execute_Bezier(u32 op, u32 diff) { + // This also make skipping drawing very effective. + framebufferManager_->SetRenderFrameBuffer(gstate_c.framebufChanged, gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + // TODO: Should this eat some cycles? Probably yes. Not sure if important. + return; + } + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + return; + } + + void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); + void *indices = NULL; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + return; + } + indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); + } + + if (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) { + DEBUG_LOG_REPORT(G3D, "Bezier + morph: %i", (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT); + } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Bezier + skinning: %i", vertTypeGetNumBoneWeights(gstate.vertType)); + } + + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + int bz_ucount = op & 0xFF; + int bz_vcount = (op >> 8) & 0xFF; + bool computeNormals = gstate.isLightingEnabled(); + bool patchFacing = gstate.patchfacing & 1; + int bytesRead = 0; + drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); + + // After drawing, we advance pointers - see SubmitPrim which does the same. + int count = bz_ucount * bz_vcount; + AdvanceVerts(gstate.vertType, count, bytesRead); +} + +void GPU_Vulkan::Execute_Spline(u32 op, u32 diff) { + // This also make skipping drawing very effective. + framebufferManager_->SetRenderFrameBuffer(gstate_c.framebufChanged, gstate_c.skipDrawReason); + if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { + // TODO: Should this eat some cycles? Probably yes. Not sure if important. + return; + } + + if (!Memory::IsValidAddress(gstate_c.vertexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr); + return; + } + + void *control_points = Memory::GetPointerUnchecked(gstate_c.vertexAddr); + void *indices = NULL; + if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { + if (!Memory::IsValidAddress(gstate_c.indexAddr)) { + ERROR_LOG_REPORT(G3D, "Bad index address %08x!", gstate_c.indexAddr); + return; + } + indices = Memory::GetPointerUnchecked(gstate_c.indexAddr); + } + + if (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) { + DEBUG_LOG_REPORT(G3D, "Spline + morph: %i", (gstate.vertType & GE_VTYPE_MORPHCOUNT_MASK) >> GE_VTYPE_MORPHCOUNT_SHIFT); + } + if (vertTypeIsSkinningEnabled(gstate.vertType)) { + DEBUG_LOG_REPORT(G3D, "Spline + skinning: %i", vertTypeGetNumBoneWeights(gstate.vertType)); + } + + int sp_ucount = op & 0xFF; + int sp_vcount = (op >> 8) & 0xFF; + int sp_utype = (op >> 16) & 0x3; + int sp_vtype = (op >> 18) & 0x3; + GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + bool computeNormals = gstate.isLightingEnabled(); + bool patchFacing = gstate.patchfacing & 1; + u32 vertType = gstate.vertType; + 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); + + // After drawing, we advance pointers - see SubmitPrim which does the same. + int count = sp_ucount * sp_vcount; + AdvanceVerts(gstate.vertType, count, bytesRead); +} + void GPU_Vulkan::Execute_Region(u32 op, u32 diff) { gstate_c.framebufChanged = true; gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index 6161251161..935f086382 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -77,6 +77,8 @@ public: void Execute_Vaddr(u32 op, u32 diff); void Execute_Iaddr(u32 op, u32 diff); void Execute_Prim(u32 op, u32 diff); + void Execute_Bezier(u32 op, u32 diff); + void Execute_Spline(u32 op, u32 diff); void Execute_VertexType(u32 op, u32 diff); void Execute_Region(u32 op, u32 diff); void Execute_Scissor(u32 op, u32 diff); From b436aef32a6824356ecb52deeca3e3a76cb4549c Mon Sep 17 00:00:00 2001 From: xebra Date: Sun, 22 Jan 2017 23:47:19 +0900 Subject: [PATCH 14/17] [spline/bezier]Avoid to set shader id bits if option of hardware tessellation is off. --- GPU/Common/ShaderId.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index beb0eaabe0..06f4b440c0 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -132,11 +132,13 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform) id.SetBit(VS_BIT_NORM_REVERSE, gstate.areNormalsReversed()); id.SetBit(VS_BIT_HAS_TEXCOORD, hasTexcoord); - id.SetBit(VS_BIT_BEZIER, doBezier); - id.SetBit(VS_BIT_SPLINE, doSpline); - id.SetBit(VS_BIT_HAS_COLOR_TESS, hasColorTess); - id.SetBit(VS_BIT_HAS_TEXCOORD_TESS, hasTexcoordTess); - id.SetBit(VS_BIT_NORM_REVERSE_TESS, gstate.isPatchNormalsReversed()); + if (g_Config.bHardwareTessellation) { + id.SetBit(VS_BIT_BEZIER, doBezier); + id.SetBit(VS_BIT_SPLINE, doSpline); + id.SetBit(VS_BIT_HAS_COLOR_TESS, hasColorTess); + id.SetBit(VS_BIT_HAS_TEXCOORD_TESS, hasTexcoordTess); + id.SetBit(VS_BIT_NORM_REVERSE_TESS, gstate.isPatchNormalsReversed()); + } } id.SetBit(VS_BIT_FLATSHADE, doFlatShading); From 7530e35ab69791e6626f9c41039f11b372b31d98 Mon Sep 17 00:00:00 2001 From: xebra Date: Sun, 22 Jan 2017 23:51:53 +0900 Subject: [PATCH 15/17] Fix 64bits shader dirty flags properly. --- GPU/GLES/ShaderManager.h | 120 +++++++++++++-------------------------- 1 file changed, 39 insertions(+), 81 deletions(-) diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index 2ffa930a12..c76a99eab9 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -120,100 +120,58 @@ public: int u_spline_type_v; }; -enum { - _DIRTY_PROJMATRIX = 0, - _DIRTY_PROJTHROUGHMATRIX = 1, - _DIRTY_FOGCOLOR = 2, - _DIRTY_FOGCOEF = 3, - _DIRTY_TEXENV = 4, - _DIRTY_ALPHACOLORREF = 5, +enum : uint64_t { + DIRTY_PROJMATRIX = 1ULL << 0, + DIRTY_PROJTHROUGHMATRIX = 1ULL << 1, + DIRTY_FOGCOLOR = 1ULL << 2, + DIRTY_FOGCOEF = 1ULL << 3, + DIRTY_TEXENV = 1ULL << 4, + DIRTY_ALPHACOLORREF = 1ULL << 5, // 1 << 6 is free! Wait, not anymore... - _DIRTY_STENCILREPLACEVALUE = 6, + DIRTY_STENCILREPLACEVALUE = 1ULL << 6, - _DIRTY_ALPHACOLORMASK = 7, - _DIRTY_LIGHT0 = 8, - _DIRTY_LIGHT1 = 9, - _DIRTY_LIGHT2 = 10, - _DIRTY_LIGHT3 = 11, + DIRTY_ALPHACOLORMASK = 1ULL << 7, + DIRTY_LIGHT0 = 1ULL << 8, + DIRTY_LIGHT1 = 1ULL << 9, + DIRTY_LIGHT2 = 1ULL << 10, + DIRTY_LIGHT3 = 1ULL << 11, - _DIRTY_MATDIFFUSE = 12, - _DIRTY_MATSPECULAR = 13, - _DIRTY_MATEMISSIVE = 14, - _DIRTY_AMBIENT = 15, - _DIRTY_MATAMBIENTALPHA = 16, + DIRTY_MATDIFFUSE = 1ULL << 12, + DIRTY_MATSPECULAR = 1ULL << 13, + DIRTY_MATEMISSIVE = 1ULL << 14, + DIRTY_AMBIENT = 1ULL << 15, + DIRTY_MATAMBIENTALPHA = 1ULL << 16, - _DIRTY_SHADERBLEND = 17, // Used only for in-shader blending. + DIRTY_SHADERBLEND = 1ULL << 17, // Used only for in-shader blending. - _DIRTY_UVSCALEOFFSET = 18, // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" + DIRTY_UVSCALEOFFSET = 1ULL << 18, // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" // Texclamp is fairly rare so let's share it's bit with DIRTY_DEPTHRANGE. - _DIRTY_TEXCLAMP = 19, - _DIRTY_DEPTHRANGE = 19, + DIRTY_TEXCLAMP = 1ULL << 19, + DIRTY_DEPTHRANGE = 1ULL << 19, - _DIRTY_WORLDMATRIX = 21, - _DIRTY_VIEWMATRIX = 22, // Maybe we'll fold this into projmatrix eventually - _DIRTY_TEXMATRIX = 23, - _DIRTY_BONEMATRIX0 = 24, - _DIRTY_BONEMATRIX1 = 25, - _DIRTY_BONEMATRIX2 = 26, - _DIRTY_BONEMATRIX3 = 27, - _DIRTY_BONEMATRIX4 = 28, - _DIRTY_BONEMATRIX5 = 29, - _DIRTY_BONEMATRIX6 = 30, - _DIRTY_BONEMATRIX7 = 31, + DIRTY_WORLDMATRIX = 1ULL << 21, + DIRTY_VIEWMATRIX = 1ULL << 22, // Maybe we'll fold this into projmatrix eventually + DIRTY_TEXMATRIX = 1ULL << 23, + DIRTY_BONEMATRIX0 = 1ULL << 24, + DIRTY_BONEMATRIX1 = 1ULL << 25, + DIRTY_BONEMATRIX2 = 1ULL << 26, + DIRTY_BONEMATRIX3 = 1ULL << 27, + DIRTY_BONEMATRIX4 = 1ULL << 28, + DIRTY_BONEMATRIX5 = 1ULL << 29, + DIRTY_BONEMATRIX6 = 1ULL << 30, + DIRTY_BONEMATRIX7 = 1ULL << 31, - _DIRTY_BEZIERCOUNTU = 32, // Used only for hardware tessellation - _DIRTY_SPLINECOUNTU = 33, // Used only for hardware tessellation - _DIRTY_SPLINECOUNTV = 34, // Used only for hardware tessellation - _DIRTY_SPLINETYPEU = 35, // Used only for hardware tessellation - _DIRTY_SPLINETYPEV = 36, // Used only for hardware tessellation + DIRTY_BEZIERCOUNTU = 1ULL << 32, // Used only for hardware tessellation + DIRTY_SPLINECOUNTU = 1ULL << 33, // Used only for hardware tessellation + DIRTY_SPLINECOUNTV = 1ULL << 34, // Used only for hardware tessellation + DIRTY_SPLINETYPEU = 1ULL << 35, // Used only for hardware tessellation + DIRTY_SPLINETYPEV = 1ULL << 36, // Used only for hardware tessellation - __END_OF_LINE__DIRTY = 64, - - DIRTY_ALL = -1 + DIRTY_ALL = 0xFFFFFFFFFFFFFFFF }; -#define FLAG_BIT64(x) (1ULL << x) - -#define DIRTY_PROJMATRIX FLAG_BIT64(_DIRTY_PROJMATRIX) -#define DIRTY_PROJTHROUGHMATRIX FLAG_BIT64(_DIRTY_PROJTHROUGHMATRIX) -#define DIRTY_FOGCOLOR FLAG_BIT64(_DIRTY_FOGCOLOR) -#define DIRTY_FOGCOEF FLAG_BIT64(_DIRTY_FOGCOEF) -#define DIRTY_TEXENV FLAG_BIT64(_DIRTY_TEXENV) -#define DIRTY_ALPHACOLORREF FLAG_BIT64(_DIRTY_ALPHACOLORREF) -#define DIRTY_STENCILREPLACEVALUE FLAG_BIT64(_DIRTY_STENCILREPLACEVALUE) -#define DIRTY_ALPHACOLORMASK FLAG_BIT64(_DIRTY_ALPHACOLORMASK) -#define DIRTY_LIGHT0 FLAG_BIT64(_DIRTY_LIGHT0) -#define DIRTY_LIGHT1 FLAG_BIT64(_DIRTY_LIGHT1) -#define DIRTY_LIGHT2 FLAG_BIT64(_DIRTY_LIGHT2) -#define DIRTY_LIGHT3 FLAG_BIT64(_DIRTY_LIGHT3) -#define DIRTY_MATDIFFUSE FLAG_BIT64(_DIRTY_MATDIFFUSE) -#define DIRTY_MATSPECULAR FLAG_BIT64(_DIRTY_MATSPECULAR) -#define DIRTY_MATEMISSIVE FLAG_BIT64(_DIRTY_MATEMISSIVE) -#define DIRTY_AMBIENT FLAG_BIT64(_DIRTY_AMBIENT) -#define DIRTY_MATAMBIENTALPHA FLAG_BIT64(_DIRTY_MATAMBIENTALPHA) -#define DIRTY_SHADERBLEND FLAG_BIT64(_DIRTY_SHADERBLEND) -#define DIRTY_UVSCALEOFFSET FLAG_BIT64(_DIRTY_UVSCALEOFFSET) -#define DIRTY_TEXCLAMP FLAG_BIT64(_DIRTY_TEXCLAMP) -#define DIRTY_DEPTHRANGE FLAG_BIT64(_DIRTY_DEPTHRANGE) -#define DIRTY_WORLDMATRIX FLAG_BIT64(_DIRTY_WORLDMATRIX) -#define DIRTY_VIEWMATRIX FLAG_BIT64(_DIRTY_VIEWMATRIX) -#define DIRTY_TEXMATRIX FLAG_BIT64(_DIRTY_TEXMATRIX) -#define DIRTY_BONEMATRIX0 FLAG_BIT64(_DIRTY_BONEMATRIX0) -#define DIRTY_BONEMATRIX1 FLAG_BIT64(_DIRTY_BONEMATRIX1) -#define DIRTY_BONEMATRIX2 FLAG_BIT64(_DIRTY_BONEMATRIX2) -#define DIRTY_BONEMATRIX3 FLAG_BIT64(_DIRTY_BONEMATRIX3) -#define DIRTY_BONEMATRIX4 FLAG_BIT64(_DIRTY_BONEMATRIX4) -#define DIRTY_BONEMATRIX5 FLAG_BIT64(_DIRTY_BONEMATRIX5) -#define DIRTY_BONEMATRIX6 FLAG_BIT64(_DIRTY_BONEMATRIX6) -#define DIRTY_BONEMATRIX7 FLAG_BIT64(_DIRTY_BONEMATRIX7) -#define DIRTY_BEZIERCOUNTU FLAG_BIT64(_DIRTY_BEZIERCOUNTU) -#define DIRTY_SPLINECOUNTU FLAG_BIT64(_DIRTY_SPLINECOUNTU) -#define DIRTY_SPLINECOUNTV FLAG_BIT64(_DIRTY_SPLINECOUNTV) -#define DIRTY_SPLINETYPEU FLAG_BIT64(_DIRTY_SPLINETYPEU) -#define DIRTY_SPLINETYPEV FLAG_BIT64(_DIRTY_SPLINETYPEV) - // Real public interface class Shader { From 54a2dcd4420b98e2b3af5a2f32a936381549a970 Mon Sep 17 00:00:00 2001 From: xebra Date: Sun, 22 Jan 2017 23:57:47 +0900 Subject: [PATCH 16/17] [spline/bezier]Add some comments and fix to avoid division, use multiply instead. --- GPU/Common/SplineCommon.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 3745ff1fac..2da30a2031 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -223,22 +223,24 @@ static void spline_knot(int n, int type, float *knot) { } } +// Prepare mesh of one patch for "Instanced Tessellation". static void TessellateSplinePatchHardware(u8 *&dest, u16 *indices, int &count, const SplinePatchLocal &spatch) { SimpleVertex *&vertices = (SimpleVertex*&)dest; + float inv_u = 1.0f / (float)spatch.tess_u; + float inv_v = 1.0f / (float)spatch.tess_v; + + // Generating simple input vertices for the spline-computing vertex shader. for (int tile_v = 0; tile_v < spatch.tess_v + 1; ++tile_v) { for (int tile_u = 0; tile_u < spatch.tess_u + 1; ++tile_u) { - float u = ((float)tile_u / (float)spatch.tess_u); - float v = ((float)tile_v / (float)spatch.tess_v); - SimpleVertex &vert = vertices[tile_v * (spatch.tess_u + 1) + tile_u]; + vert.pos.x = (float)tile_u * inv_u; + vert.pos.y = (float)tile_v * inv_v; - vert.pos.x = u; - vert.pos.y = v; - + // TODO: Move to shader uniform and unify this method spline and bezier if necessary. // For compute normal - vert.nrm.x = 1.0f / (float)spatch.tess_u; - vert.nrm.y = 1.0f / (float)spatch.tess_v; + vert.nrm.x = inv_u; + vert.nrm.y = inv_v; } } @@ -800,18 +802,20 @@ static void _BezierPatchHighQuality(u8 *&dest, u16 *&indices, int &count, int te dest += (tess_u + 1) * (tess_v + 1) * sizeof(SimpleVertex); } +// Prepare mesh of one patch for "Instanced Tessellation". static void TesselateBezierPatchHardware(u8 *&dest, u16 *indices, int &count, int tess_u, int tess_v, GEPatchPrimType primType) { SimpleVertex *&vertices = (SimpleVertex*&)dest; + float inv_u = 1.0f / (float)tess_u; + float inv_v = 1.0f / (float)tess_v; + + // Generating simple input vertices for the bezier-computing vertex shader. for (int tile_v = 0; tile_v < tess_v + 1; ++tile_v) { for (int tile_u = 0; tile_u < tess_u + 1; ++tile_u) { - float u = ((float)tile_u / (float)tess_u); - float v = ((float)tile_v / (float)tess_v); - SimpleVertex &vert = vertices[tile_v * (tess_u + 1) + tile_u]; - vert.pos.x = u; - vert.pos.y = v; + vert.pos.x = (float)tile_u * inv_u; + vert.pos.y = (float)tile_v * inv_v; } } From f111eede854465faf4f816a5cf934091b3f2e191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 23 Jan 2017 14:31:44 +0100 Subject: [PATCH 17/17] Some GLSL syntax fixes --- GPU/GLES/VertexShaderGenerator.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index 45189a9d92..159c3946e2 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -337,11 +337,11 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { for (int i = 2; i <= 4; i++) { // Define 3 types vec2, vec3, vec4 WRITE(p, "vec%d tess_sample(in vec%d points[16], in vec2 weights[4]) {\n", i, i); - WRITE(p, " vec%d pos = vec%d(0);\n", i, i); + WRITE(p, " vec%d pos = vec%d(0.0);\n", i, i); 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)\n"); + WRITE(p, " if (f != 0.0)\n"); WRITE(p, " pos = pos + f * points[i * 4 + j];\n"); WRITE(p, " }\n"); WRITE(p, " }\n"); @@ -395,13 +395,13 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { WRITE(p, " vec2 f31 = t1 / (knot[3] - knot[1]);\n"); WRITE(p, " vec2 f42 = t2 / (knot[4] - knot[2]);\n"); WRITE(p, " vec2 f32 = t2 / (knot[3] - knot[2]);\n"); - WRITE(p, " vec2 a = (1 - f30)*(1 - f31);\n"); + WRITE(p, " vec2 a = (1.0 - f30)*(1.0 - f31);\n"); WRITE(p, " vec2 b = (f31*f41);\n"); - WRITE(p, " vec2 c = (1 - f41)*(1 - f42);\n"); + WRITE(p, " vec2 c = (1.0 - f41)*(1.0 - f42);\n"); WRITE(p, " vec2 d = (f42*f52);\n"); WRITE(p, " weights[0] = a - (a*f32);\n"); - WRITE(p, " weights[1] = 1 - a - b + ((a + b + c - 1)*f32);\n"); - WRITE(p, " weights[2] = b + ((1 - b - c - d)*f32);\n"); + WRITE(p, " weights[1] = vec2(1.0) - a - b + ((a + b + c - vec2(1.0))*f32);\n"); + WRITE(p, " weights[2] = b + ((vec2(1.0) - b - c - d)*f32);\n"); WRITE(p, " weights[3] = d*f32;\n"); WRITE(p, "}\n"); } @@ -467,9 +467,9 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { WRITE(p, " vec2 weights[4];\n"); if (doBezier) { // Bernstein 3D - WRITE(p, " weights[0] = (1 - tess_pos) * (1 - tess_pos) * (1 - tess_pos);\n"); - WRITE(p, " weights[1] = 3 * tess_pos * (1 - tess_pos) * (1 - tess_pos);\n"); - WRITE(p, " weights[2] = 3 * tess_pos * tess_pos * (1 - tess_pos);\n"); + 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, " ivec2 spline_num_patches = ivec2(u_spline_count_u - 3, u_spline_count_v - 3);\n"); @@ -496,10 +496,10 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { if (doBezier) { // Bernstein derivative WRITE(p, " vec2 bernderiv[4];\n"); - WRITE(p, " bernderiv[0] = -3 * (tess_pos - 1) * (tess_pos - 1); \n"); - WRITE(p, " bernderiv[1] = 9 * tess_pos * tess_pos - 12 * tess_pos + 3; \n"); - WRITE(p, " bernderiv[2] = 3 * (2 - 3 * tess_pos) * tess_pos; \n"); - WRITE(p, " bernderiv[3] = 3 * tess_pos * tess_pos; \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, " vec2 bernderiv_u[4];\n"); WRITE(p, " vec2 bernderiv_v[4];\n"); @@ -511,8 +511,8 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) { WRITE(p, " vec3 du = tess_sample(_pos, bernderiv_u);\n"); WRITE(p, " vec3 dv = tess_sample(_pos, bernderiv_v);\n"); } else if (doSpline) { - WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0);\n"); - WRITE(p, " vec2 tess_next_v = vec2(0, normal.y);\n"); + WRITE(p, " vec2 tess_next_u = vec2(normal.x, 0.0);\n"); + WRITE(p, " vec2 tess_next_v = vec2(0.0, normal.y);\n"); // Right WRITE(p, " vec2 tess_pos_r = tess_pos + tess_next_u;\n"); WRITE(p, " spline_knot(spline_num_patches, spline_type, knots, patch_pos);\n");