Merge pull request #9226 from hrydgard/cleaned-up-hw-tess

Cleaned up branch of Xebra's hw tess
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2017-01-23 15:26:12 +01:00
commit ce2feb2a67
25 files changed
+979 -118

No files matched your search

+1
View File
@@ -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),
+1
View File
@@ -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;
+13
View File
@@ -87,4 +87,17 @@ 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:
// 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;
};
+20
View File
@@ -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,14 @@ void ComputeVertexShaderID(ShaderID *id_out, u32 vertType, bool useHWTransform)
id.SetBit(VS_BIT_NORM_REVERSE, gstate.areNormalsReversed());
id.SetBit(VS_BIT_HAS_TEXCOORD, hasTexcoord);
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);
+9 -3
View File
@@ -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
};
+162 -54
View File
@@ -223,6 +223,41 @@ 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) {
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;
// TODO: Move to shader uniform and unify this method spline and bezier if necessary.
// For compute normal
vert.nrm.x = inv_u;
vert.nrm.y = inv_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 +802,37 @@ 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) {
SimpleVertex &vert = vertices[tile_v * (tess_u + 1) + tile_u];
vert.pos.x = (float)tile_u * inv_u;
vert.pos.y = (float)tile_v * inv_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:
@@ -781,6 +847,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 +881,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 +912,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;
@@ -856,9 +931,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;
@@ -890,11 +986,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);
@@ -922,35 +1014,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;
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;
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;
}
}
@@ -969,12 +1071,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;
+8
View File
@@ -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
+12
View File
@@ -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
+89
View File
@@ -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;
+2
View File
@@ -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);
+63 -1
View File
@@ -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,57 @@ 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_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) {
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_TO_EDGE);
glTexParameteri(GL_TEXTURE_1D, 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);
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_TO_EDGE);
glTexParameteri(GL_TEXTURE_1D, 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);
prevSizeCol = sizeColor;
} else {
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, sizeColor, GL_RGBA, GL_FLOAT, (GLfloat*)col);
}
glActiveTexture(GL_TEXTURE0);
}
+14
View File
@@ -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;
};
};
+123 -1
View File
@@ -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,129 @@ 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_Region(u32 op, u32 diff) {
gstate_c.framebufChanged = true;
gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY;
+2
View File
@@ -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);
+59 -9
View File
@@ -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;
}
@@ -413,7 +437,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;
@@ -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,10 +740,24 @@ 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()
: 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 +781,7 @@ void ShaderManager::Clear() {
linkedShaderCache_.clear();
fsCache_.clear();
vsCache_.clear();
globalDirty_ = 0xFFFFFFFF;
globalDirty_ = DIRTY_ALL;
lastFSID_.set_invalid();
lastVSID_.set_invalid();
DirtyShader();
@@ -747,7 +797,7 @@ void ShaderManager::DirtyShader() {
lastFSID_.set_invalid();
lastVSID_.set_invalid();
DirtyLastShader();
globalDirty_ = 0xFFFFFFFF;
globalDirty_ = DIRTY_ALL;
shaderSwitchDirty_ = 0;
}
@@ -847,7 +897,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;
+53 -39
View File
@@ -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.
@@ -110,52 +110,66 @@ 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 {
DIRTY_PROJMATRIX = (1 << 0),
DIRTY_PROJTHROUGHMATRIX = (1 << 1),
DIRTY_FOGCOLOR = (1 << 2),
DIRTY_FOGCOEF = (1 << 3),
DIRTY_TEXENV = (1 << 4),
DIRTY_ALPHACOLORREF = (1 << 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 = (1 << 6),
DIRTY_STENCILREPLACEVALUE = 1ULL << 6,
DIRTY_ALPHACOLORMASK = (1 << 7),
DIRTY_LIGHT0 = (1 << 8),
DIRTY_LIGHT1 = (1 << 9),
DIRTY_LIGHT2 = (1 << 10),
DIRTY_LIGHT3 = (1 << 11),
DIRTY_ALPHACOLORMASK = 1ULL << 7,
DIRTY_LIGHT0 = 1ULL << 8,
DIRTY_LIGHT1 = 1ULL << 9,
DIRTY_LIGHT2 = 1ULL << 10,
DIRTY_LIGHT3 = 1ULL << 11,
DIRTY_MATDIFFUSE = (1 << 12),
DIRTY_MATSPECULAR = (1 << 13),
DIRTY_MATEMISSIVE = (1 << 14),
DIRTY_AMBIENT = (1 << 15),
DIRTY_MATAMBIENTALPHA = (1 << 16),
DIRTY_MATDIFFUSE = 1ULL << 12,
DIRTY_MATSPECULAR = 1ULL << 13,
DIRTY_MATEMISSIVE = 1ULL << 14,
DIRTY_AMBIENT = 1ULL << 15,
DIRTY_MATAMBIENTALPHA = 1ULL << 16,
DIRTY_SHADERBLEND = (1 << 17), // Used only for in-shader blending.
DIRTY_SHADERBLEND = 1ULL << 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 = 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 = (1 << 19),
DIRTY_DEPTHRANGE = (1 << 19),
DIRTY_TEXCLAMP = 1ULL << 19,
DIRTY_DEPTHRANGE = 1ULL << 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 = 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_ALL = 0xFFFFFFFF
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
DIRTY_ALL = 0xFFFFFFFFFFFFFFFF
};
// Real public interface
@@ -191,7 +205,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 +243,8 @@ private:
ShaderID lastVSID_;
LinkedShader *lastShader_;
u32 globalDirty_;
u32 shaderSwitchDirty_;
u64 globalDirty_;
u64 shaderSwitchDirty_;
char *codeBuffer_;
typedef std::map<ShaderID, Shader *> FSCache;
+216 -9
View File
@@ -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,88 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) {
WRITE(p, "}\n\n");
}
// Hardware tessellation
if (doBezier || doSpline) {
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");
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.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.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.0 - f30)*(1.0 - f31);\n");
WRITE(p, " vec2 b = (f31*f41);\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] = 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");
}
}
WRITE(p, "void main() {\n");
if (!useHWTransform) {
@@ -355,12 +443,117 @@ 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" : "");
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, %s, 0).xy;\n", index);
if (hasColor && hasColorTess)
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");
WRITE(p, " vec2 weights[4];\n");
if (doBezier) {
// Bernstein 3D
WRITE(p, " weights[0] = (1.0 - tess_pos) * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
WRITE(p, " weights[1] = 3.0 * tess_pos * (1.0 - tess_pos) * (1.0 - tess_pos);\n");
WRITE(p, " weights[2] = 3.0 * tess_pos * tess_pos * (1.0 - tess_pos);\n");
WRITE(p, " weights[3] = tess_pos * tess_pos * tess_pos;\n");
} else if (doSpline) {
WRITE(p, " 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, %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)
if (doBezier) {
// Bernstein derivative
WRITE(p, " vec2 bernderiv[4];\n");
WRITE(p, " bernderiv[0] = -3.0 * (tess_pos - 1.0) * (tess_pos - 1.0); \n");
WRITE(p, " bernderiv[1] = 9.0 * tess_pos * tess_pos - 12.0 * tess_pos + 3.0; \n");
WRITE(p, " bernderiv[2] = 3.0 * (2.0 - 3.0 * tess_pos) * tess_pos; \n");
WRITE(p, " bernderiv[3] = 3.0 * tess_pos * tess_pos; \n");
WRITE(p, " 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.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");
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 +645,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 +768,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 +786,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");
}
+9
View File
@@ -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<GEPatchPrimType>(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;
+8
View File
@@ -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)
{
}
+12
View File
@@ -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;
};
};
+89
View File
@@ -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;
+2
View File
@@ -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);
+10 -1
View File
@@ -278,7 +278,15 @@ 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.
// graphicsSettings->Add(new CheckBox(&g_Config.bFXAA, gr->T("FXAA")));
@@ -715,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;
}
+1
View File
@@ -116,6 +116,7 @@ private:
bool postProcEnable_;
bool resolutionEnable_;
bool bloomHackEnable_;
bool bezierChoiceDisable_;
};
class SettingInfoMessage : public UI::TextView {
+1 -1
View File
@@ -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",