diff --git a/Core/AVIDump.cpp b/Core/AVIDump.cpp index e118c390db..6188a2bda8 100644 --- a/Core/AVIDump.cpp +++ b/Core/AVIDump.cpp @@ -34,6 +34,10 @@ extern "C" { #include "GPU/Common/GPUDebugInterface.h" +#include "Core/ELF/ParamSFO.h" +#include "Core/HLE/sceKernelTime.h" +#include "StringUtils.h" + #ifdef USE_FFMPEG #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 28, 1) @@ -86,10 +90,16 @@ bool AVIDump::CreateAVI() { #ifdef USE_FFMPEG AVCodec* codec = nullptr; + // Use gameID_EmulatedTimestamp for filename + std::string discID = g_paramSFO.GetDiscID(); + std::string video_file_name = StringFromFormat("%s%s_%s.avi", GetSysDirectory(DIRECTORY_VIDEO).c_str(), discID.c_str(), KernelTimeNowFormatted().c_str()).c_str(); + s_format_context = avformat_alloc_context(); std::stringstream s_file_index_str; - s_file_index_str << s_file_index; - snprintf(s_format_context->filename, sizeof(s_format_context->filename), "%s", (GetSysDirectory(DIRECTORY_VIDEO) + "framedump" + s_file_index_str.str() + ".avi").c_str()); + s_file_index_str << video_file_name; + + snprintf(s_format_context->filename, sizeof(s_format_context->filename), "%s", s_file_index_str.str().c_str()); + INFO_LOG(COMMON, "Recording Video to: %s", s_format_context->filename); // Make sure that the path exists if (!File::Exists(GetSysDirectory(DIRECTORY_VIDEO))) File::CreateDir(GetSysDirectory(DIRECTORY_VIDEO)); @@ -214,6 +224,7 @@ void AVIDump::AddFrame() if (error) ERROR_LOG(G3D, "Error while encoding video: %d", error); #endif + delete[] flipbuffer; } void AVIDump::Stop() { diff --git a/Core/Config.cpp b/Core/Config.cpp index 962e594aac..bff4eaf3ca 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -361,6 +361,7 @@ static ConfigSetting generalSettings[] = { ConfigSetting("UseFFV1", &g_Config.bUseFFV1, false), ConfigSetting("DumpFrames", &g_Config.bDumpFrames, false), ConfigSetting("DumpAudio", &g_Config.bDumpAudio, false), + ConfigSetting("SaveLoadResetsAVdumping", &g_Config.bSaveLoadResetsAVdumping, false), ConfigSetting("StateSlot", &g_Config.iCurrentStateSlot, 0, true, true), ConfigSetting("RewindFlipFrequency", &g_Config.iRewindFlipFrequency, 0, true, true), diff --git a/Core/Config.h b/Core/Config.h index 662ef45fba..6f2f975385 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -105,6 +105,7 @@ public: bool bUseFFV1; bool bDumpFrames; bool bDumpAudio; + bool bSaveLoadResetsAVdumping; bool bEnableLogging; bool bDumpDecryptedEboot; bool bFullscreenOnDoubleclick; diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 9e7b10a8ea..86111980f1 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -35,6 +35,9 @@ #include "Core/System.h" #ifndef MOBILE_DEVICE #include "Core/WaveFile.h" +#include "Core/ELF/ParamSFO.h" +#include "Core/HLE/sceKernelTime.h" +#include "StringUtils.h" #endif #include "Core/HLE/__sceAudio.h" #include "Core/HLE/sceAudio.h" @@ -332,7 +335,7 @@ void __AudioSetOutputFrequency(int freq) { // Mix samples from the various audio channels into a single sample queue. // This single sample queue is where __AudioMix should read from. If the sample queue is full, we should // just sleep the main emulator thread a little. -void __AudioUpdate() { +void __AudioUpdate(bool resetRecording) { // Audio throttle doesn't really work on the PSP since the mixing intervals are so closely tied // to the CPU. Much better to throttle the frame rate on frame display and just throw away audio // if the buffer somehow gets full. @@ -385,11 +388,25 @@ void __AudioUpdate() { if (g_Config.bEnableSound) { resampler.PushSamples(mixBuffer, hwBlockSize); #ifndef MOBILE_DEVICE + if (g_Config.bSaveLoadResetsAVdumping && resetRecording) { + __StopLogAudio(); + std::string discID = g_paramSFO.GetDiscID(); + std::string audio_file_name = StringFromFormat("%s%s_%s.wav", GetSysDirectory(DIRECTORY_AUDIO).c_str(), discID.c_str(), KernelTimeNowFormatted().c_str()).c_str(); + INFO_LOG(COMMON, "Restarted audio recording to: %s", audio_file_name.c_str()); + if (!File::Exists(GetSysDirectory(DIRECTORY_AUDIO))) + File::CreateDir(GetSysDirectory(DIRECTORY_AUDIO)); + File::CreateEmptyFile(audio_file_name); + __StartLogAudio(audio_file_name); + } if (!m_logAudio) { if (g_Config.bDumpAudio) { - std::string audio_file_name = GetSysDirectory(DIRECTORY_AUDIO) + "audiodump.wav"; + // Use gameID_EmulatedTimestamp for filename + std::string discID = g_paramSFO.GetDiscID(); + std::string audio_file_name = StringFromFormat("%s%s_%s.wav", GetSysDirectory(DIRECTORY_AUDIO).c_str(), discID.c_str(), KernelTimeNowFormatted().c_str()).c_str(); + INFO_LOG(COMMON,"Recording audio to: %s", audio_file_name.c_str()); // Create the path just in case it doesn't exist - File::CreateDir(GetSysDirectory(DIRECTORY_AUDIO)); + if (!File::Exists(GetSysDirectory(DIRECTORY_AUDIO))) + File::CreateDir(GetSysDirectory(DIRECTORY_AUDIO)); File::CreateEmptyFile(audio_file_name); __StartLogAudio(audio_file_name); } @@ -447,3 +464,7 @@ void __StopLogAudio() { } } #endif + +void WAVDump::Reset() { + __AudioUpdate(true); +} diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index 87365fb361..39f83d8401 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -33,7 +33,7 @@ struct AudioDebugStats { void __AudioInit(); void __AudioDoState(PointerWrap &p); -void __AudioUpdate(); +void __AudioUpdate(bool resetRecording = false); void __AudioShutdown(); void __AudioSetOutputFrequency(int freq); @@ -49,3 +49,9 @@ void __PushExternalAudio(const s32 *audio, int numSamples); // Should not be us // Audio Dumping stuff void __StartLogAudio(const std::string& filename); void __StopLogAudio(); + +class WAVDump +{ +public: + static void Reset(); +}; diff --git a/Core/HLE/sceKernelTime.cpp b/Core/HLE/sceKernelTime.cpp index 087e8f115b..b74772df3b 100644 --- a/Core/HLE/sceKernelTime.cpp +++ b/Core/HLE/sceKernelTime.cpp @@ -31,6 +31,7 @@ #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceRtc.h" #include "Core/MemMap.h" +#include "StringUtils.h" // The time when the game started. static time_t start_time; @@ -171,3 +172,19 @@ u32 sceKernelLibcGettimeofday(u32 timeAddr, u32 tzAddr) hleReSchedule("libc timeofday"); return 0; } + +std::string KernelTimeNowFormatted() { + time_t emulatedTime = (time_t)start_time + (u32)(CoreTiming::GetGlobalTimeUs() / 1000000ULL); + tm* timePtr = localtime(&emulatedTime); + bool DST = timePtr->tm_isdst; + u8 seconds = timePtr->tm_sec; + u8 minutes = timePtr->tm_min; + u8 hours = timePtr->tm_hour; + if (DST) + hours = timePtr->tm_hour + 1; + u8 days = timePtr->tm_mday; + u8 months = timePtr->tm_mon + 1; + u16 years = timePtr->tm_year + 1900; + std::string timestamp = StringFromFormat("%04d-%02d-%02d_%02d-%02d-%02d", years, months, days, hours, minutes, seconds); + return timestamp; +} \ No newline at end of file diff --git a/Core/HLE/sceKernelTime.h b/Core/HLE/sceKernelTime.h index 2cbc5ca2fb..d973c20d40 100644 --- a/Core/HLE/sceKernelTime.h +++ b/Core/HLE/sceKernelTime.h @@ -27,6 +27,7 @@ int sceKernelSysClock2USec(u32 sysclockPtr, u32 highPtr, u32 lowPtr); int sceKernelSysClock2USecWide(u32 lowClock, u32 highClock, u32 lowPtr, u32 highPtr); u64 sceKernelUSec2SysClockWide(u32 usec); u32 sceKernelLibcClock(); +std::string KernelTimeNowFormatted(); void __KernelTimeInit(); void __KernelTimeDoState(PointerWrap &p); diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 55786770a6..29d4eed86c 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -47,6 +47,11 @@ #include "GPU/GPUInterface.h" #include "GPU/GPUState.h" +#ifndef MOBILE_DEVICE +#include "Core/AVIDump.h" +#include "Core/HLE/__sceAudio.h" +#endif + namespace SaveState { struct SaveStart @@ -592,6 +597,17 @@ namespace SaveState callbackMessage = sc->T("Loaded State"); callbackResult = true; hasLoadedState = true; +#ifndef MOBILE_DEVICE + if (g_Config.bSaveLoadResetsAVdumping) { + if (g_Config.bDumpFrames) { + AVIDump::Stop(); + AVIDump::Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight); + } + if (g_Config.bDumpAudio) { + WAVDump::Reset(); + } + } +#endif } else if (result == CChunkFileReader::ERROR_BROKEN_STATE) { HandleFailure(); callbackMessage = i18nLoadFailure; @@ -616,6 +632,17 @@ namespace SaveState if (result == CChunkFileReader::ERROR_NONE) { callbackMessage = sc->T("Saved State"); callbackResult = true; +#ifndef MOBILE_DEVICE + if (g_Config.bSaveLoadResetsAVdumping) { + if (g_Config.bDumpFrames) { + AVIDump::Stop(); + AVIDump::Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight); + } + if (g_Config.bDumpAudio) { + WAVDump::Reset(); + } + } +#endif } else if (result == CChunkFileReader::ERROR_BROKEN_STATE) { HandleFailure(); callbackMessage = i18nSaveFailure; diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 245800754a..c227483621 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1173,18 +1173,11 @@ void TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETextureForm memcpy(out + outPitch * y, texptr + bufw * sizeof(u16) * y, w * sizeof(u16)); } } - } else if (h >= 8) { + } else if (h >= 8 && !expandTo32bit) { // Note: this is always safe since h must be a power of 2, so a multiple of 8. - if (!expandTo32bit) { - UnswizzleFromMem((u32 *)out, outPitch, texptr, bufw, h, 2); - if (reverseColors) { - ReverseColors(out, out, format, h * outPitch / 2, useBGRA); - } - } else if (expandTo32bit) { - UnswizzleFromMem((u32 *)out, outPitch / 2, texptr, bufw, h, 2); - for (int y = h - 1; y >= 0; --y) { - ConvertFormatToRGBA8888(format, (u32 *)(out + outPitch * y), (const u16 *)(out + outPitch / 2 * y), w); - } + UnswizzleFromMem((u32 *)out, outPitch, texptr, bufw, h, 2); + if (reverseColors) { + ReverseColors(out, out, format, h * outPitch / 2, useBGRA); } } else { // We don't have enough space for all rows in out, so use a temp buffer. diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 5ddcc18347..4cd6f9e40d 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -73,13 +73,15 @@ int DecFmtSize(u8 fmt) { case DEC_U16_2: return 4; case DEC_U16_3: return 8; case DEC_U16_4: return 8; - case DEC_U8A_2: return 4; - case DEC_U16A_2: return 4; default: return 0; } } +void DecVtxFormat::ComputeID() { + id = w0fmt | (w1fmt << 4) | (uvfmt << 8) | (c0fmt << 12) | (c1fmt << 16) | (nrmfmt << 20) | (posfmt << 24); +} + void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBound, u16 *indexUpperBound) { // Find index bounds. Could cache this in display lists. // Also, this could be greatly sped up with SSE2/NEON, although rarely a bottleneck. @@ -1152,6 +1154,8 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options, decFmt.stride = decOff; + decFmt.ComputeID(); + size = align(size, biggest); onesize_ = size; size *= morphcount; diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index 9222090480..fbe60c1756 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -43,6 +43,7 @@ // Can write code to easily bind these using OpenGL, or read these manually. // No morph support, that is taken care of by the VertexDecoder. +// Keep this in 4 bits. enum { DEC_NONE, DEC_FLOAT_1, @@ -59,8 +60,6 @@ enum { DEC_U16_2, DEC_U16_3, DEC_U16_4, - DEC_U8A_2, - DEC_U16A_2, }; int DecFmtSize(u8 fmt); @@ -74,6 +73,9 @@ struct DecVtxFormat { u8 nrmfmt; u8 nrmoff; u8 posfmt; u8 posoff; short stride; + + uint32_t id; + void ComputeID(); }; struct TransformedVertex @@ -297,21 +299,6 @@ public: } break; - case DEC_U8A_2: - { - const u8 *b = (const u8 *)(data_ + decFmt_.uvoff); - uv[0] = (float)b[0]; - uv[1] = (float)b[1]; - } - break; - - case DEC_U16A_2: - { - const u16 *p = (const u16 *)(data_ + decFmt_.uvoff); - uv[0] = (float)p[0]; - uv[1] = (float)p[1]; - } - break; default: ERROR_LOG_REPORT_ONCE(fmtuv, G3D, "Reader: Unsupported UV Format %d", decFmt_.uvfmt); memset(uv, 0, sizeof(float) * 2); diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index e8e6ba8580..15d67838ee 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -175,8 +175,6 @@ static const DeclTypeInfo VComp[] = { { DXGI_FORMAT_UNKNOWN, "UNUSED_DEC_U16_2" }, // DEC_U16_2, { DXGI_FORMAT_R16G16B16A16_UNORM ,"D3DDECLTYPE_USHORT4N "}, // DEC_U16_3, { DXGI_FORMAT_R16G16B16A16_UNORM ,"D3DDECLTYPE_USHORT4N "}, // DEC_U16_4, - { DXGI_FORMAT_UNKNOWN, "UNUSED_DEC_U8A_2"}, // DEC_U8A_2, - { DXGI_FORMAT_UNKNOWN, "UNUSED_DEC_U16A_2" }, // DEC_U16A_2, }; static void VertexAttribSetup(D3D11_INPUT_ELEMENT_DESC * VertexElement, u8 fmt, u8 offset, const char *semantic, u8 semantic_index = 0) { @@ -190,7 +188,7 @@ static void VertexAttribSetup(D3D11_INPUT_ELEMENT_DESC * VertexElement, u8 fmt, ID3D11InputLayout *DrawEngineD3D11::SetupDecFmtForDraw(D3D11VertexShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt) { // TODO: Instead of one for each vshader, we can reduce it to one for each type of shader // that reads TEXCOORD or not, etc. Not sure if worth it. - InputLayoutKey key{ vshader, pspFmt }; + InputLayoutKey key{ vshader, decFmt.id }; ID3D11InputLayout *inputLayout = inputLayoutMap_.Get(key); if (inputLayout) { return inputLayout; @@ -251,22 +249,6 @@ ID3D11InputLayout *DrawEngineD3D11::SetupDecFmtForDraw(D3D11VertexShader *vshade } } -void DrawEngineD3D11::SetupVertexDecoder(u32 vertType) { - SetupVertexDecoderInternal(vertType); -} - -inline void DrawEngineD3D11::SetupVertexDecoderInternal(u32 vertType) { - // As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it - // into the top of the verttype where there are unused bits. - const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24); - - // If vtype has changed, setup the vertex decoder. - if (vertTypeID != lastVType_) { - dec_ = GetVertexDecoder(vertTypeID); - lastVType_ = vertTypeID; - } -} - void DrawEngineD3D11::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) { if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) Flush(); @@ -278,7 +260,7 @@ void DrawEngineD3D11::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, prevPrim_ = prim; } - SetupVertexDecoderInternal(vertType); + SetupVertexDecoder(vertType); *bytesRead = vertexCount * dec_->VertexSize(); diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index 7838c79cea..b0c77b3e4b 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -121,9 +121,6 @@ public: void BeginFrame(); - void SetupVertexDecoder(u32 vertType); - void SetupVertexDecoderInternal(u32 vertType); - // So that this can be inlined void Flush() { if (!numDrawCalls) @@ -170,11 +167,11 @@ private: struct InputLayoutKey { D3D11VertexShader *vshader; - u32 vertType; + u32 decFmtId; bool operator <(const InputLayoutKey &other) const { - if (vertType < other.vertType) + if (decFmtId < other.decFmtId) return true; - if (vertType > other.vertType) + if (decFmtId > other.decFmtId) return false; return vshader < other.vshader; } diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index 3c402a2025..760285b5de 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -76,16 +76,16 @@ struct D3D11CommandTableEntry { // This table gets crunched into a faster form by init. static const D3D11CommandTableEntry commandTable[] = { // Changes that dirty the current texture. - { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_TexSize0 }, + { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_TexSize0 }, { GE_CMD_STENCILTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_STENCILREPLACEVALUE | DIRTY_FOGCOEF | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE }, // These are combined in D3D11 // Changing the vertex type requires us to flush. - { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPU_D3D11::Execute_VertexType }, + { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType }, { GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Prim }, - { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Bezier }, - { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_Spline }, + { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier }, + { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline }, // Changes that trigger data copies. Only flushing on change for LOADCLUT must be a bit of a hack... { GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_D3D11::Execute_LoadClut }, @@ -202,10 +202,10 @@ GPU_D3D11::~GPU_D3D11() { void GPU_D3D11::UpdateCmdInfo() { if (g_Config.bSoftwareSkinning) { cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE; - cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_D3D11::Execute_VertexTypeSkinning; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexTypeSkinning; } else { cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE; - cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_D3D11::Execute_VertexType; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType; } CheckGPUFeatures(); @@ -443,37 +443,6 @@ void GPU_D3D11::ExecuteOp(u32 op, u32 diff) { } } -void GPU_D3D11::Execute_VertexType(u32 op, u32 diff) { - if (diff) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) { - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); - } -} - -void GPU_D3D11::Execute_VertexTypeSkinning(u32 op, u32 diff) { - // Don't flush when weight count changes, unless morph is enabled. - if ((diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) || (op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { - // Restore and flush - gstate.vertType ^= diff; - Flush(); - gstate.vertType ^= diff; - if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - // In this case, we may be doing weights and morphs. - // Update any bone matrix uniforms so it uses them correctly. - if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { - gstate_c.Dirty(gstate_c.deferredVertTypeDirty); - gstate_c.deferredVertTypeDirty = 0; - } - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - } - if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); -} - void GPU_D3D11::Execute_Prim(u32 op, u32 diff) { // This drives all drawing. All other state we just buffer up, then we apply it only // when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization. @@ -547,154 +516,6 @@ void GPU_D3D11::Execute_Prim(u32 op, u32 diff) { AdvanceVerts(vertexType, count, bytesRead); } -void GPU_D3D11::Execute_Bezier(u32 op, u32 diff) { - Flush(); - - // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - - // This also make skipping drawing very effective. - framebufferManagerD3D11_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), 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(); - SetDrawType(DRAW_BEZIER, PatchPrimToPrim(patchPrim)); - - 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.Dirty(DIRTY_VERTEXSHADER_STATE); - gstate_c.bezier = true; - if (gstate_c.spline_count_u != bz_ucount) { - gstate_c.Dirty(DIRTY_BEZIERSPLINE); - gstate_c.spline_count_u = bz_ucount; - } - } - - int bytesRead = 0; - UpdateUVScaleOffset(); - drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); - - if (gstate_c.bezier) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - 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_D3D11::Execute_Spline(u32 op, u32 diff) { - Flush(); - - // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - - // This also make skipping drawing very effective. - framebufferManagerD3D11_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), 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(); - SetDrawType(DRAW_SPLINE, PatchPrimToPrim(patchPrim)); - 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.Dirty(DIRTY_VERTEXSHADER_STATE); - gstate_c.spline = true; - bool countsChanged = gstate_c.spline_count_u != sp_ucount || gstate_c.spline_count_v != sp_vcount; - bool typesChanged = gstate_c.spline_type_u != sp_utype || gstate_c.spline_type_v != sp_vtype; - if (countsChanged || typesChanged) { - gstate_c.Dirty(DIRTY_BEZIERSPLINE); - gstate_c.spline_count_u = sp_ucount; - gstate_c.spline_count_v = sp_vcount; - gstate_c.spline_type_u = sp_utype; - gstate_c.spline_type_v = sp_vtype; - } - } - int bytesRead = 0; - UpdateUVScaleOffset(); - drawEngine_.SubmitSpline(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), sp_ucount, sp_vcount, sp_utype, sp_vtype, patchPrim, computeNormals, patchFacing, vertType, &bytesRead); - - if (gstate_c.spline) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - 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_D3D11::Execute_TexSize0(u32 op, u32 diff) { - // Render to texture may have overridden the width/height. - // Don't reset it unless the size is different / the texture has changed. - if (diff || gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS)) { - gstate_c.curTextureWidth = gstate.getTextureWidth(0); - gstate_c.curTextureHeight = gstate.getTextureHeight(0); - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - // We will need to reset the texture now. - gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); - } -} - void GPU_D3D11::Execute_LoadClut(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); textureCacheD3D11_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes()); diff --git a/GPU/D3D11/GPU_D3D11.h b/GPU/D3D11/GPU_D3D11.h index a528d41169..9d7ff6bc59 100644 --- a/GPU/D3D11/GPU_D3D11.h +++ b/GPU/D3D11/GPU_D3D11.h @@ -68,11 +68,6 @@ public: }; 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_TexSize0(u32 op, u32 diff); void Execute_LoadClut(u32 op, u32 diff); // Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend. diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 68d5d837fb..f452c945c0 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -155,9 +155,6 @@ static const DeclTypeInfo VComp[] = { {0, "UNUSED_DEC_U16_2" }, // DEC_U16_2, {D3DDECLTYPE_USHORT4N ,"D3DDECLTYPE_USHORT4N "}, // DEC_U16_3, {D3DDECLTYPE_USHORT4N ,"D3DDECLTYPE_USHORT4N "}, // DEC_U16_4, - // Not supported in regular DX9 so faking, will cause graphics bugs until worked around - {0,"UNUSED_DEC_U8A_2"}, // DEC_U8A_2, - {0,"UNUSED_DEC_U16A_2" }, // DEC_U16A_2, }; static void VertexAttribSetup(D3DVERTEXELEMENT9 * VertexElement, u8 fmt, u8 offset, u8 usage, u8 usage_index = 0) { diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index bc85e33e0f..a9359d46dd 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -60,16 +60,16 @@ struct D3D9CommandTableEntry { // This table gets crunched into a faster form by init. static const D3D9CommandTableEntry commandTable[] = { // Changes that dirty the current texture. - { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_DX9::Execute_TexSize0 }, + { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_TexSize0 }, { GE_CMD_STENCILTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_STENCILREPLACEVALUE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE }, // Changing the vertex type requires us to flush. - { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPU_DX9::Execute_VertexType }, + { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType }, { GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPU_DX9::Execute_Prim }, - { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_DX9::Execute_Bezier }, - { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_DX9::Execute_Spline }, + { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier }, + { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline }, // Changes that trigger data copies. Only flushing on change for LOADCLUT must be a bit of a hack... { GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_DX9::Execute_LoadClut }, @@ -171,10 +171,10 @@ GPU_DX9::GPU_DX9(GraphicsContext *gfxCtx, Draw::DrawContext *draw) void GPU_DX9::UpdateCmdInfo() { if (g_Config.bSoftwareSkinning) { cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE; - cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_DX9::Execute_VertexTypeSkinning; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexTypeSkinning; } else { cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE; - cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_DX9::Execute_VertexType; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType; } CheckGPUFeatures(); @@ -408,37 +408,6 @@ void GPU_DX9::ExecuteOp(u32 op, u32 diff) { } } -void GPU_DX9::Execute_VertexType(u32 op, u32 diff) { - if (diff) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) { - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); - } -} - -void GPU_DX9::Execute_VertexTypeSkinning(u32 op, u32 diff) { - // Don't flush when weight count changes, unless morph is enabled. - if ((diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) || (op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { - // Restore and flush - gstate.vertType ^= diff; - Flush(); - gstate.vertType ^= diff; - if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - // In this case, we may be doing weights and morphs. - // Update any bone matrix uniforms so it uses them correctly. - if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { - gstate_c.Dirty(gstate_c.deferredVertTypeDirty); - gstate_c.deferredVertTypeDirty = 0; - } - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - } - if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE); -} - void GPU_DX9::Execute_Prim(u32 op, u32 diff) { // This drives all drawing. All other state we just buffer up, then we apply it only // when it's time to draw. As most PSP games set state redundantly ALL THE TIME, this is a huge optimization. @@ -511,121 +480,6 @@ void GPU_DX9::Execute_Prim(u32 op, u32 diff) { AdvanceVerts(vertexType, count, bytesRead); } -void GPU_DX9::Execute_Bezier(u32 op, u32 diff) { - Flush(); - - // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - - // This also make skipping drawing very effective. - framebufferManagerDX9_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), 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(); - SetDrawType(DRAW_BEZIER, PatchPrimToPrim(patchPrim)); - int bz_ucount = op & 0xFF; - int bz_vcount = (op >> 8) & 0xFF; - bool computeNormals = gstate.isLightingEnabled(); - bool patchFacing = gstate.patchfacing & 1; - int bytesRead = 0; - UpdateUVScaleOffset(); - 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) { - Flush(); - - // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - - // This also make skipping drawing very effective. - framebufferManagerDX9_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), 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(); - SetDrawType(DRAW_SPLINE, PatchPrimToPrim(patchPrim)); - bool computeNormals = gstate.isLightingEnabled(); - bool patchFacing = gstate.patchfacing & 1; - u32 vertType = gstate.vertType; - int bytesRead = 0; - UpdateUVScaleOffset(); - 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_TexSize0(u32 op, u32 diff) { - // Render to texture may have overridden the width/height. - // Don't reset it unless the size is different / the texture has changed. - if (diff || gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS)) { - gstate_c.curTextureWidth = gstate.getTextureWidth(0); - gstate_c.curTextureHeight = gstate.getTextureHeight(0); - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - // We will need to reset the texture now. - gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); - } -} - void GPU_DX9::Execute_LoadClut(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); textureCacheDX9_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes()); diff --git a/GPU/Directx9/GPU_DX9.h b/GPU/Directx9/GPU_DX9.h index 2f15d48197..9c0da49c11 100644 --- a/GPU/Directx9/GPU_DX9.h +++ b/GPU/Directx9/GPU_DX9.h @@ -69,10 +69,6 @@ public: }; 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_TexSize0(u32 op, u32 diff); void Execute_LoadClut(u32 op, u32 diff); diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index fe1f1e0653..6034d491c3 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -228,8 +228,6 @@ static const GlTypeInfo GLComp[] = { {GL_UNSIGNED_SHORT, 2, GL_TRUE},// DEC_U16_2, {GL_UNSIGNED_SHORT, 3, GL_TRUE},// DEC_U16_3, {GL_UNSIGNED_SHORT, 4, GL_TRUE},// DEC_U16_4, - {GL_UNSIGNED_BYTE, 2, GL_FALSE},// DEC_U8A_2, - {GL_UNSIGNED_SHORT, 2, GL_FALSE},// DEC_U16A_2, }; static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) { diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 781f4c5cd7..eae91ae8d1 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -63,16 +63,16 @@ struct GLESCommandTableEntry { // TODO: Share this table between the backends. Will have to make another indirection for the function pointers though.. static const GLESCommandTableEntry commandTable[] = { // Changes that dirty the current texture. - { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, DIRTY_UVSCALEOFFSET, &GPU_GLES::Execute_TexSize0 }, + { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_TexSize0 }, { GE_CMD_STENCILTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_STENCILREPLACEVALUE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE }, // Changing the vertex type requires us to flush. - { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPU_GLES::Execute_VertexType }, + { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType }, { GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPU_GLES::Execute_Prim }, - { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_GLES::Execute_Bezier }, - { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_GLES::Execute_Spline }, + { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier }, + { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline }, // Changes that trigger data copies. Only flushing on change for LOADCLUT must be a bit of a hack... { GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_GLES::Execute_LoadClut }, @@ -461,10 +461,10 @@ inline void GPU_GLES::UpdateVsyncInterval(bool force) { void GPU_GLES::UpdateCmdInfo() { if (g_Config.bSoftwareSkinning) { cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE; - cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_GLES::Execute_VertexTypeSkinning; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexTypeSkinning; } else { cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE; - cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_GLES::Execute_VertexType; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType; } } @@ -688,184 +688,6 @@ void GPU_GLES::Execute_Prim(u32 op, u32 diff) { AdvanceVerts(gstate.vertType, count, bytesRead); } -void GPU_GLES::Execute_VertexType(u32 op, u32 diff) { - if (diff) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) { - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); - } -} - -void GPU_GLES::Execute_VertexTypeSkinning(u32 op, u32 diff) { - // Don't flush when weight count changes, unless morph is enabled. - if ((diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) || (op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { - // Restore and flush - gstate.vertType ^= diff; - Flush(); - gstate.vertType ^= diff; - if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - // In this case, we may be doing weights and morphs. - // Update any bone matrix uniforms so it uses them correctly. - if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { - gstate_c.Dirty(gstate_c.deferredVertTypeDirty); - gstate_c.deferredVertTypeDirty = 0; - } - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - } - if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); -} - -void GPU_GLES::Execute_Bezier(u32 op, u32 diff) { - Flush(); - - // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - - // This also make skipping drawing very effective. - framebufferManagerGL_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), 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(); - SetDrawType(DRAW_BEZIER, PatchPrimToPrim(patchPrim)); - - 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.Dirty(DIRTY_VERTEXSHADER_STATE); - gstate_c.bezier = true; - if (gstate_c.spline_count_u != bz_ucount) { - gstate_c.Dirty(DIRTY_BEZIERSPLINE); - gstate_c.spline_count_u = bz_ucount; - } - } - - int bytesRead = 0; - UpdateUVScaleOffset(); - drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); - - if (gstate_c.bezier) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - 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) { - Flush(); - - // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - - // This also make skipping drawing very effective. - framebufferManagerGL_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), 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(); - SetDrawType(DRAW_SPLINE, PatchPrimToPrim(patchPrim)); - 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.Dirty(DIRTY_VERTEXSHADER_STATE); - gstate_c.spline = true; - bool countsChanged = gstate_c.spline_count_u != sp_ucount || gstate_c.spline_count_v != sp_vcount; - bool typesChanged = gstate_c.spline_type_u != sp_utype || gstate_c.spline_type_v != sp_vtype; - if (countsChanged || typesChanged) { - gstate_c.Dirty(DIRTY_BEZIERSPLINE); - gstate_c.spline_count_u = sp_ucount; - gstate_c.spline_count_v = sp_vcount; - gstate_c.spline_type_u = sp_utype; - gstate_c.spline_type_v = sp_vtype; - } - } - - int bytesRead = 0; - UpdateUVScaleOffset(); - drawEngine_.SubmitSpline(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), sp_ucount, sp_vcount, sp_utype, sp_vtype, patchPrim, computeNormals, patchFacing, vertType, &bytesRead); - - if (gstate_c.spline) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - 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_TexSize0(u32 op, u32 diff) { - // Render to texture may have overridden the width/height. - // Don't reset it unless the size is different / the texture has changed. - if (diff || gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS)) { - gstate_c.curTextureWidth = gstate.getTextureWidth(0); - gstate_c.curTextureHeight = gstate.getTextureHeight(0); - gstate_c.Dirty(DIRTY_UVSCALEOFFSET | DIRTY_TEXTURE_PARAMS); - } -} - void GPU_GLES::Execute_LoadClut(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); textureCacheGL_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes()); diff --git a/GPU/GLES/GPU_GLES.h b/GPU/GLES/GPU_GLES.h index a9662cf271..e3ffb81d4b 100644 --- a/GPU/GLES/GPU_GLES.h +++ b/GPU/GLES/GPU_GLES.h @@ -71,12 +71,7 @@ public: }; void Execute_Prim(u32 op, u32 diff); - void Execute_Bezier(u32 op, u32 diff); - void Execute_Spline(u32 op, u32 diff); void Execute_LoadClut(u32 op, u32 diff); - void Execute_VertexType(u32 op, u32 diff); - void Execute_VertexTypeSkinning(u32 op, u32 diff); - void Execute_TexSize0(u32 op, u32 diff); // Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend. std::vector DebugGetShaderIDs(DebugShaderType shader) override; diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 3cfaf9badb..01e7d70547 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1329,8 +1329,55 @@ void GPUCommon::Execute_TexLevel(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_TEXTURE_PARAMS | DIRTY_FRAGMENTSHADER_STATE); } +void GPUCommon::Execute_TexSize0(u32 op, u32 diff) { + // Render to texture may have overridden the width/height. + // Don't reset it unless the size is different / the texture has changed. + if (diff || gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS)) { + gstate_c.curTextureWidth = gstate.getTextureWidth(0); + gstate_c.curTextureHeight = gstate.getTextureHeight(0); + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + // We will need to reset the texture now. + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); + } +} + +void GPUCommon::Execute_VertexType(u32 op, u32 diff) { + if (diff) + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); + if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) { + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + if (diff & GE_VTYPE_THROUGH_MASK) + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); + } +} + +void GPUCommon::Execute_VertexTypeSkinning(u32 op, u32 diff) { + // Don't flush when weight count changes, unless morph is enabled. + if ((diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) || (op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { + // Restore and flush + gstate.vertType ^= diff; + Flush(); + gstate.vertType ^= diff; + if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + // In this case, we may be doing weights and morphs. + // Update any bone matrix uniforms so it uses them correctly. + if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { + gstate_c.Dirty(gstate_c.deferredVertTypeDirty); + gstate_c.deferredVertTypeDirty = 0; + } + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); + } + if (diff & GE_VTYPE_THROUGH_MASK) + gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); +} + void GPUCommon::Execute_Bezier(u32 op, u32 diff) { - Flush(); + drawEngineCommon_->DispatchFlush(); + + // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + // This also make skipping drawing very effective. framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { @@ -1361,20 +1408,41 @@ void GPUCommon::Execute_Bezier(u32 op, u32 diff) { } GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + SetDrawType(DRAW_BEZIER, PatchPrimToPrim(patchPrim)); + 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.Dirty(DIRTY_VERTEXSHADER_STATE); + gstate_c.bezier = true; + if (gstate_c.spline_count_u != bz_ucount) { + gstate_c.Dirty(DIRTY_BEZIERSPLINE); + gstate_c.spline_count_u = bz_ucount; + } + } + int bytesRead = 0; + UpdateUVScaleOffset(); drawEngineCommon_->SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); + if (gstate_c.bezier) + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); + 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 GPUCommon::Execute_Spline(u32 op, u32 diff) { - Flush(); + drawEngineCommon_->DispatchFlush(); + + // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. + gstate_c.Dirty(DIRTY_UVSCALEOFFSET); + // This also make skipping drawing very effective. framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { @@ -1409,12 +1477,33 @@ void GPUCommon::Execute_Spline(u32 op, u32 diff) { int sp_utype = (op >> 16) & 0x3; int sp_vtype = (op >> 18) & 0x3; GEPatchPrimType patchPrim = gstate.getPatchPrimitiveType(); + SetDrawType(DRAW_SPLINE, PatchPrimToPrim(patchPrim)); 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.Dirty(DIRTY_VERTEXSHADER_STATE); + gstate_c.spline = true; + bool countsChanged = gstate_c.spline_count_u != sp_ucount || gstate_c.spline_count_v != sp_vcount; + bool typesChanged = gstate_c.spline_type_u != sp_utype || gstate_c.spline_type_v != sp_vtype; + if (countsChanged || typesChanged) { + gstate_c.Dirty(DIRTY_BEZIERSPLINE); + gstate_c.spline_count_u = sp_ucount; + gstate_c.spline_count_v = sp_vcount; + gstate_c.spline_type_u = sp_utype; + gstate_c.spline_type_v = sp_vtype; + } + } + int bytesRead = 0; + UpdateUVScaleOffset(); drawEngineCommon_->SubmitSpline(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), sp_ucount, sp_vcount, sp_utype, sp_vtype, patchPrim, computeNormals, patchFacing, vertType, &bytesRead); + if (gstate_c.spline) + gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); + gstate_c.spline = false; + // After drawing, we advance pointers - see SubmitPrim which does the same. int count = sp_ucount * sp_vcount; AdvanceVerts(gstate.vertType, count, bytesRead); diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index d3b53ad514..c4d22dc904 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -100,11 +100,15 @@ public: void Execute_Ret(u32 op, u32 diff); void Execute_End(u32 op, u32 diff); + void Execute_VertexType(u32 op, u32 diff); + void Execute_VertexTypeSkinning(u32 op, u32 diff); + void Execute_Bezier(u32 op, u32 diff); void Execute_Spline(u32 op, u32 diff); void Execute_BoundingBox(u32 op, u32 diff); void Execute_BlockTransferStart(u32 op, u32 diff); + void Execute_TexSize0(u32 op, u32 diff); void Execute_TexLevel(u32 op, u32 diff); void Execute_WorldMtxNum(u32 op, u32 diff); @@ -125,7 +129,7 @@ public: int EstimatePerVertexCost(); // Note: Not virtual! - inline void Flush(); + void Flush(); #ifdef USE_CRT_DBG #undef new @@ -214,12 +218,14 @@ public: protected: void SetDrawType(DrawType type, GEPrimitiveType prim) { if (type != lastDraw_) { + // We always flush when drawing splines/beziers so no need to do so here gstate_c.Dirty(DIRTY_UVSCALEOFFSET | DIRTY_VERTEXSHADER_STATE); lastDraw_ = type; } // Prim == RECTANGLES can cause CanUseHardwareTransform to flip, so we need to dirty. // Also, culling may be affected so dirty the raster state. if ((prim == GE_PRIM_RECTANGLES) != (lastPrim_ == GE_PRIM_RECTANGLES)) { + Flush(); gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VERTEXSHADER_STATE); lastPrim_ = prim; } diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index de25539f92..209f5eeaa4 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -361,27 +361,10 @@ void DrawEngineVulkan::EndFrame() { vertexCache_->End(); } -void DrawEngineVulkan::SetupVertexDecoder(u32 vertType) { - SetupVertexDecoderInternal(vertType); -} - -inline void DrawEngineVulkan::SetupVertexDecoderInternal(u32 vertType) { - // As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it - // into the top of the verttype where there are unused bits. - const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24); - - // If vtype has changed, setup the vertex decoder. - if (vertTypeID != lastVType_) { - dec_ = GetVertexDecoder(vertTypeID); - lastVType_ = vertTypeID; - } - if (!dec_) - Crash(); -} - void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) { - if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) + if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) { Flush(); + } // TODO: Is this the right thing to do? if (prim == GE_PRIM_KEEP_PREVIOUS) { @@ -390,7 +373,7 @@ void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, prevPrim_ = prim; } - SetupVertexDecoderInternal(vertType); + SetupVertexDecoder(vertType); *bytesRead = vertexCount * dec_->VertexSize(); if ((vertexCount < 2 && prim > 0) || (vertexCount < 3 && prim > 2 && prim != GE_PRIM_RECTANGLES)) @@ -502,7 +485,7 @@ void DrawEngineVulkan::SetLineWidth(float lineWidth) { pipelineManager_->SetLineWidth(lineWidth); } -VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone) { +VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone, bool tess) { DescriptorSetKey key; key.imageView_ = imageView; key.sampler_ = sampler; @@ -514,8 +497,6 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView _dbg_assert_(G3D, light != VK_NULL_HANDLE); _dbg_assert_(G3D, bone != VK_NULL_HANDLE); - bool tess = gstate_c.bezier || gstate_c.spline; - FrameData *frame = &frame_[vulkan_->GetCurFrame()]; // See if we already have this descriptor set cached. if (!tess) { // Don't cache descriptors for HW tessellation. @@ -627,7 +608,7 @@ VkDescriptorSet DrawEngineVulkan::GetOrCreateDescriptorSet(VkImageView imageView vkUpdateDescriptorSets(vulkan_->GetDevice(), n, writes, 0, nullptr); - if (!(gstate_c.bezier || gstate_c.spline)) // Avoid caching when HW tessellation. + if (!tess) // Again, avoid caching when HW tessellation. frame->descSets.Insert(key, desc); return desc; } @@ -667,6 +648,8 @@ void DrawEngineVulkan::DoFlush() { FrameData *frame = &frame_[vulkan_->GetCurFrame()]; + bool tess = gstate_c.bezier || gstate_c.spline; + bool textureNeedsApply = false; if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { textureCache_->SetTexture(); @@ -881,13 +864,15 @@ void DrawEngineVulkan::DoFlush() { } if (!lastPipeline_ || !gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, true); // usehwtransform + _dbg_assert_msg_(G3D, vshader->UseHWTransform(), "Bad vshader"); + if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, true); if (!pipeline) { // Already logged, let's bail out. return; @@ -908,7 +893,7 @@ void DrawEngineVulkan::DoFlush() { dirtyUniforms_ |= shaderManager_->UpdateUniforms(); UpdateUBOs(frame); - VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf); + VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf, tess); { PROFILE_THIS_SCOPE("renderman_q"); @@ -921,7 +906,7 @@ void DrawEngineVulkan::DoFlush() { if (useElements) { if (!ibuf) ibOffset = (uint32_t)frame->pushIndex->Push(decIndex, sizeof(uint16_t) * indexGen.VertexCount(), &ibuf); - int numInstances = (gstate_c.bezier || gstate_c.spline) ? numPatches : 1; + int numInstances = tess ? numPatches : 1; renderManager->DrawIndexed(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, numInstances, VK_INDEX_TYPE_UINT16); } else { renderManager->Draw(pipelineLayout_, ds, 3, dynamicUBOOffsets, vbuf, vbOffset, vertexCount); @@ -979,13 +964,14 @@ void DrawEngineVulkan::DoFlush() { sampler = nullSampler_; } if (!lastPipeline_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE) || prim != lastPrim_) { - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform); + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, false); // usehwtransform + _dbg_assert_msg_(G3D, !vshader->UseHWTransform(), "Bad vshader"); if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } Draw::NativeObject object = g_Config.iRenderingMode != 0 ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, false); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, false); if (!pipeline) { // Already logged, let's bail out. return; @@ -1008,7 +994,7 @@ void DrawEngineVulkan::DoFlush() { // Even if the first draw is through-mode, make sure we at least have one copy of these uniforms buffered UpdateUBOs(frame); - VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf); + VkDescriptorSet ds = GetOrCreateDescriptorSet(imageView, sampler, baseBuf, lightBuf, boneBuf, tess); const uint32_t dynamicUBOOffsets[3] = { baseUBOOffset, lightUBOOffset, boneUBOOffset, }; diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 8c0bcff4c4..b9709f261f 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -140,9 +140,6 @@ public: void DeviceLost(); void DeviceRestore(VulkanContext *vulkan, Draw::DrawContext *draw); - void SetupVertexDecoder(u32 vertType); - void SetupVertexDecoderInternal(u32 vertType); - // So that this can be inlined void Flush() { if (!numDrawCalls) @@ -193,7 +190,7 @@ private: void DoFlush(); void UpdateUBOs(FrameData *frame); - VkDescriptorSet GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone); + VkDescriptorSet GetOrCreateDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone, bool tess); VulkanContext *vulkan_; Draw::DrawContext *draw_; diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 1aba78d76f..bb25eef794 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -58,16 +58,16 @@ GPU_Vulkan::CommandInfo GPU_Vulkan::cmdInfo_[256]; // This table gets crunched into a faster form by init. static const VulkanCommandTableEntry commandTable[] = { // Changes that dirty the current texture. - { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_Vulkan::Execute_TexSize0 }, + { GE_CMD_TEXSIZE0, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPUCommon::Execute_TexSize0 }, { GE_CMD_STENCILTEST, FLAG_FLUSHBEFOREONCHANGE, DIRTY_STENCILREPLACEVALUE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE }, // Changing the vertex type requires us to flush. - { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPU_Vulkan::Execute_VertexType }, + { GE_CMD_VERTEXTYPE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GPUCommon::Execute_VertexType }, { GE_CMD_PRIM, FLAG_EXECUTE, 0, &GPU_Vulkan::Execute_Prim }, - { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_Vulkan::Execute_Bezier }, - { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPU_Vulkan::Execute_Spline }, + { GE_CMD_BEZIER, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Bezier }, + { GE_CMD_SPLINE, FLAG_FLUSHBEFORE | FLAG_EXECUTE, 0, &GPUCommon::Execute_Spline }, // Changes that trigger data copies. Only flushing on change for LOADCLUT must be a bit of a hack... { GE_CMD_LOADCLUT, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTE, 0, &GPU_Vulkan::Execute_LoadClut }, @@ -372,10 +372,10 @@ void GPU_Vulkan::UpdateVsyncInterval(bool force) { void GPU_Vulkan::UpdateCmdInfo() { if (g_Config.bSoftwareSkinning) { cmdInfo_[GE_CMD_VERTEXTYPE].flags &= ~FLAG_FLUSHBEFOREONCHANGE; - cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_Vulkan::Execute_VertexTypeSkinning; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexTypeSkinning; } else { cmdInfo_[GE_CMD_VERTEXTYPE].flags |= FLAG_FLUSHBEFOREONCHANGE; - cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPU_Vulkan::Execute_VertexType; + cmdInfo_[GE_CMD_VERTEXTYPE].func = &GPUCommon::Execute_VertexType; } } @@ -555,248 +555,11 @@ void GPU_Vulkan::Execute_Prim(u32 op, u32 diff) { AdvanceVerts(gstate.vertType, count, bytesRead); } -void GPU_Vulkan::Execute_VertexType(u32 op, u32 diff) { - if (diff) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) { - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); - } -} - -void GPU_Vulkan::Execute_VertexTypeSkinning(u32 op, u32 diff) { - // Don't flush when weight count changes, unless morph is enabled. - if ((diff & ~GE_VTYPE_WEIGHTCOUNT_MASK) || (op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { - // Restore and flush - gstate.vertType ^= diff; - Flush(); - gstate.vertType ^= diff; - if (diff & (GE_VTYPE_TC_MASK | GE_VTYPE_THROUGH_MASK)) - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - // In this case, we may be doing weights and morphs. - // Update any bone matrix uniforms so it uses them correctly. - if ((op & GE_VTYPE_MORPHCOUNT_MASK) != 0) { - gstate_c.Dirty(gstate_c.deferredVertTypeDirty); - gstate_c.deferredVertTypeDirty = 0; - } - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - } - if (diff & GE_VTYPE_THROUGH_MASK) - gstate_c.Dirty(DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_FRAGMENTSHADER_STATE); -} - -void GPU_Vulkan::Execute_Bezier(u32 op, u32 diff) { - Flush(); - - // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - - // This also make skipping drawing very effective. - framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), 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(); - SetDrawType(DRAW_BEZIER, PatchPrimToPrim(patchPrim)); - - 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.Dirty(DIRTY_VERTEXSHADER_STATE); - gstate_c.bezier = true; - if (gstate_c.spline_count_u != bz_ucount) { - gstate_c.Dirty(DIRTY_BEZIERSPLINE); - gstate_c.spline_count_u = bz_ucount; - } - } - - int bytesRead = 0; - UpdateUVScaleOffset(); - drawEngine_.SubmitBezier(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), bz_ucount, bz_vcount, patchPrim, computeNormals, patchFacing, gstate.vertType, &bytesRead); - - if (gstate_c.bezier) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - 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_Vulkan::Execute_Spline(u32 op, u32 diff) { - Flush(); - - // We don't dirty on normal changes anymore as we prescale, but it's needed for splines/bezier. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET); - - // This also make skipping drawing very effective. - framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), 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(); - SetDrawType(DRAW_SPLINE, PatchPrimToPrim(patchPrim)); - 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.Dirty(DIRTY_VERTEXSHADER_STATE); - gstate_c.spline = true; - bool countsChanged = gstate_c.spline_count_u != sp_ucount || gstate_c.spline_count_v != sp_vcount; - bool typesChanged = gstate_c.spline_type_u != sp_utype || gstate_c.spline_type_v != sp_vtype; - if (countsChanged || typesChanged) { - gstate_c.Dirty(DIRTY_BEZIERSPLINE); - gstate_c.spline_count_u = sp_ucount; - gstate_c.spline_count_v = sp_vcount; - gstate_c.spline_type_u = sp_utype; - gstate_c.spline_type_v = sp_vtype; - } - } - - int bytesRead = 0; - UpdateUVScaleOffset(); - drawEngine_.SubmitSpline(control_points, indices, gstate.getPatchDivisionU(), gstate.getPatchDivisionV(), sp_ucount, sp_vcount, sp_utype, sp_vtype, patchPrim, computeNormals, patchFacing, vertType, &bytesRead); - - if (gstate_c.spline) - gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE); - 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_Vulkan::Execute_TexSize0(u32 op, u32 diff) { - // Render to texture may have overridden the width/height. - // Don't reset it unless the size is different / the texture has changed. - if (diff || gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS)) { - gstate_c.curTextureWidth = gstate.getTextureWidth(0); - gstate_c.curTextureHeight = gstate.getTextureHeight(0); - // We will need to reset the texture now. - gstate_c.Dirty(DIRTY_UVSCALEOFFSET | DIRTY_TEXTURE_PARAMS); - } -} - void GPU_Vulkan::Execute_LoadClut(u32 op, u32 diff) { gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); textureCacheVulkan_->LoadClut(gstate.getClutAddress(), gstate.getClutLoadBytes()); } -void GPU_Vulkan::Execute_BoneMtxNum(u32 op, u32 diff) { - // This is almost always followed by GE_CMD_BONEMATRIXDATA. - const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); - u32 *dst = (u32 *)(gstate.boneMatrix + (op & 0x7F)); - const int end = 12 * 8 - (op & 0x7F); - int i = 0; - - // If we can't use software skinning, we have to flush and dirty. - while ((src[i] >> 24) == GE_CMD_BONEMATRIXDATA) { - const u32 newVal = src[i] << 8; - if (dst[i] != newVal) { - Flush(); - dst[i] = newVal; - } - if (++i >= end) { - break; - } - } - - const int numPlusCount = (op & 0x7F) + i; - for (int num = op & 0x7F; num < numPlusCount; num += 12) { - gstate_c.Dirty(DIRTY_BONEMATRIX0 << (num / 12)); - } - - const int count = i; - gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | ((op + count) & 0x7F); - - // Skip over the loaded data, it's done now. - UpdatePC(currentList->pc, currentList->pc + count * 4); - currentList->pc += count * 4; -} - -void GPU_Vulkan::Execute_BoneMtxData(u32 op, u32 diff) { - // Note: it's uncommon to get here now, see above. - int num = gstate.boneMatrixNumber & 0x7F; - u32 newVal = op << 8; - if (num < 96 && newVal != ((const u32 *)gstate.boneMatrix)[num]) { - // Bone matrices should NOT flush when software skinning is enabled! - Flush(); - gstate_c.Dirty(DIRTY_BONEMATRIX0 << (num / 12)); - ((u32 *)gstate.boneMatrix)[num] = newVal; - } - num++; - gstate.boneMatrixNumber = (GE_CMD_BONEMATRIXNUMBER << 24) | (num & 0x7F); -} - -void GPU_Vulkan::FastLoadBoneMatrix(u32 target) { - const int num = gstate.boneMatrixNumber & 0x7F; - const int mtxNum = num / 12; - uint32_t uniformsToDirty = DIRTY_BONEMATRIX0 << mtxNum; - if ((num - 12 * mtxNum) != 0) { - uniformsToDirty |= DIRTY_BONEMATRIX0 << ((mtxNum + 1) & 7); - } - Flush(); - gstate_c.Dirty(uniformsToDirty); - gstate.FastLoadBoneMatrix(target); -} - void GPU_Vulkan::InitDeviceObjects() { ILOG("GPU_Vulkan::InitDeviceObjects"); // Initialize framedata diff --git a/GPU/Vulkan/GPU_Vulkan.h b/GPU/Vulkan/GPU_Vulkan.h index d94c4ee768..caeb71b6e1 100644 --- a/GPU/Vulkan/GPU_Vulkan.h +++ b/GPU/Vulkan/GPU_Vulkan.h @@ -72,14 +72,7 @@ public: }; 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_TexSize0(u32 op, u32 diff); void Execute_LoadClut(u32 op, u32 diff); - void Execute_BoneMtxNum(u32 op, u32 diff); - void Execute_BoneMtxData(u32 op, u32 diff); // Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend. std::vector DebugGetShaderIDs(DebugShaderType shader) override; @@ -87,7 +80,6 @@ public: protected: void FastRunLoop(DisplayList &list) override; - void FastLoadBoneMatrix(u32 target) override; void FinishDeferred() override; private: diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 940254e7d1..f63f0de9c1 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -66,9 +66,6 @@ static const DeclTypeInfo VComp[] = { { VK_FORMAT_R16G16_UNORM, "R16G16_UNORM" }, // DEC_U16_2, { VK_FORMAT_R16G16B16A16_UNORM, "R16G16B16A16_UNORM " }, // DEC_U16_3, { VK_FORMAT_R16G16B16A16_UNORM, "R16G16B16A16_UNORM " }, // DEC_U16_4, - - { VK_FORMAT_R8G8_UINT, "R8G8_UINT" }, // DEC_U8A_2, - { VK_FORMAT_R16G16_UINT, "R16G16_UINT" }, // DEC_U16A_2, }; static void VertexAttribSetup(VkVertexInputAttributeDescription *attr, int fmt, int offset, PspAttributeLocation location) { @@ -121,7 +118,7 @@ static bool UsesBlendConstant(int factor) { static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pipelineCache, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key, - const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, float lineWidth) { + const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, float lineWidth) { bool useBlendConstant = false; VkPipelineColorBlendAttachmentState blend0 = {}; @@ -229,10 +226,10 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip VkVertexInputAttributeDescription attrs[8]; int attributeCount; if (useHwTransform) { - attributeCount = SetupVertexAttribs(attrs, vtxDec->decFmt); - vertexStride = vtxDec->decFmt.stride; + attributeCount = SetupVertexAttribs(attrs, *decFmt); + vertexStride = decFmt->stride; } else { - attributeCount = SetupVertexAttribsPretransformed(attrs, vtxDec->decFmt); + attributeCount = SetupVertexAttribsPretransformed(attrs, *decFmt); vertexStride = 36; } @@ -301,8 +298,8 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip return vulkanPipeline; } -VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) { - VulkanPipelineKey key; +VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) { + VulkanPipelineKey key{}; if (!renderPass) Crash(); @@ -311,7 +308,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layo key.useHWTransform = useHwTransform; key.vShader = vs->GetModule(); key.fShader = fs->GetModule(); - key.vtxDec = useHwTransform ? vtxDec : nullptr; + key.vtxDecId = useHwTransform ? decFmt->id : 0; auto iter = pipelines_.Get(key); if (iter) @@ -321,7 +318,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layo VulkanPipeline *pipeline = CreateVulkanPipeline( vulkan_->GetDevice(), pipelineCache_, layout, renderPass, - rasterKey, vtxDec, vs, fs, useHwTransform, lineWidth_); + rasterKey, decFmt, vs, fs, useHwTransform, lineWidth_); // Even if the result is nullptr, insert it so we don't try to create it repeatedly. pipelines_.Insert(key, pipeline); return pipeline; @@ -345,14 +342,75 @@ std::vector PipelineManagerVulkan::DebugGetObjectIDs(DebugShaderTyp return ids; } +static const char *const topologies[8] = { + "POINTLIST", + "LINELIST", + "LINESTRIP", + "TRILIST", + "TRISTRIP", + "TRIFAN", +}; + +static const char *const blendOps[8] = { + "ADD", + "SUB", + "REVSUB", + "MIN", + "MAX", +}; + +static const char *const compareOps[8] = { + "NEVER", + "<", + "==", + "<=", + ">", + ">=", + "!=", + "ALWAYS", +}; + +static const char *const stencilOps[8] = { + "KEEP", + "ZERO", + "REPLACE", + "INC_CLAMP", + "DEC_CLAMP", + "INVERT", + "INC_WRAP", + "DEC_WRAP", +}; + +static const char *const blendFactors[19] = { + "ZERO", + "ONE", + "SRC_COLOR", + "ONE_MINUS_SRC_COLOR", + "DST_COLOR", + "ONE_MINUS_DST_COLOR", + "SRC_ALPHA", + "ONE_MINUS_SRC_ALPHA", + "DST_ALPHA", + "ONE_MINUS_DST_ALPHA", + "CONSTANT_COLOR", + "ONE_MINUS_CONSTANT_COLOR", + "CONSTANT_ALPHA", + "ONE_MINUS_CONSTANT_ALPHA", + "SRC_ALPHA_SATURATE", + "SRC1_COLOR", + "ONE_MINUS_SRC1_COLOR", + "SRC1_ALPHA", + "ONE_MINUS_SRC1_ALPHA", +}; + std::string PipelineManagerVulkan::DebugGetObjectString(std::string id, DebugShaderType type, DebugShaderStringType stringType) { if (type != SHADER_TYPE_PIPELINE) return "N/A"; - VulkanPipelineKey shaderId; - shaderId.FromString(id); + VulkanPipelineKey pipelineKey; + pipelineKey.FromString(id); - VulkanPipeline *iter = pipelines_.Get(shaderId); + VulkanPipeline *iter = pipelines_.Get(pipelineKey); if (!iter) { return ""; } @@ -360,7 +418,62 @@ std::string PipelineManagerVulkan::DebugGetObjectString(std::string id, DebugSha switch (stringType) { case SHADER_STRING_SHORT_DESC: { - return StringFromFormat("%p", iter); + std::stringstream str; + str << topologies[pipelineKey.raster.topology] << " "; + if (pipelineKey.raster.blendEnable) { + str << "Blend("; + str << "C:" << blendOps[pipelineKey.raster.blendOpColor] << "/" + << blendFactors[pipelineKey.raster.srcColor] << ":" << blendFactors[pipelineKey.raster.destColor] << " "; + if (pipelineKey.raster.blendOpAlpha != VK_BLEND_OP_ADD || + pipelineKey.raster.srcAlpha != VK_BLEND_FACTOR_ONE || + pipelineKey.raster.destAlpha != VK_BLEND_FACTOR_ZERO) { + str << "A:" << blendOps[pipelineKey.raster.blendOpAlpha] << "/" + << blendFactors[pipelineKey.raster.srcColor] << ":" << blendFactors[pipelineKey.raster.destColor] << " "; + } + str << ") "; + } + if (pipelineKey.raster.colorWriteMask != 0xF) { + str << "Mask("; + for (int i = 0; i < 4; i++) { + if (pipelineKey.raster.colorWriteMask & (1 << i)) { + str << "RGBA"[i]; + } else { + str << "_"; + } + } + str << ") "; + } + if (pipelineKey.raster.depthTestEnable) { + str << "Depth("; + if (pipelineKey.raster.depthWriteEnable) + str << "W, "; + if (pipelineKey.raster.depthCompareOp) + str << compareOps[pipelineKey.raster.depthCompareOp & 7]; + str << ") "; + } + if (pipelineKey.raster.stencilTestEnable) { + str << "Stencil("; + str << compareOps[pipelineKey.raster.stencilCompareOp & 7] << " "; + str << stencilOps[pipelineKey.raster.stencilPassOp & 7] << "/"; + str << stencilOps[pipelineKey.raster.stencilFailOp & 7] << "/"; + str << stencilOps[pipelineKey.raster.stencilDepthFailOp& 7]; + str << ") "; + } + if (pipelineKey.raster.logicOpEnable) { + str << "Logic("; + str << ") "; + } + if (pipelineKey.useHWTransform) { + str << "HWX "; + } + if (pipelineKey.vtxDecId) { + str << "V("; + str << StringFromFormat("%08x", pipelineKey.vtxDecId); // TODO: Format nicer. + str << ") "; + } else { + str << "SWX "; + } + return StringFromFormat("%p: %s", iter, str.str().c_str()); } case SHADER_STRING_SOURCE_CODE: @@ -385,4 +498,4 @@ void PipelineManagerVulkan::SetLineWidth(float lineWidth) { pipelines_.Remove(key); } }); -} \ No newline at end of file +} diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index 9387ed43fe..8463033784 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -40,10 +40,10 @@ enum class PspAttributeLocation { struct VulkanPipelineKey { VulkanPipelineRasterStateKey raster; // prim is included here VkRenderPass renderPass; - bool useHWTransform; - const VertexDecoder *vtxDec; VkShaderModule vShader; VkShaderModule fShader; + uint32_t vtxDecId; + bool useHWTransform; void ToString(std::string *str) const { str->resize(sizeof(*this)); @@ -77,7 +77,7 @@ public: PipelineManagerVulkan(VulkanContext *ctx); ~PipelineManagerVulkan(); - VulkanPipeline *GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const VertexDecoder *vtxDec, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform); + VulkanPipeline *GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform); int GetNumPipelines() const { return (int)pipelines_.size(); } void Clear(); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 222b093fed..ef2f4aee8e 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -28,6 +28,7 @@ #include "util/text/utf8.h" #include "Common/Vulkan/VulkanContext.h" #include "Common/Vulkan/VulkanMemory.h" +#include "Common/Log.h" #include "Common/Common.h" #include "Core/Config.h" #include "Core/Reporting.h" @@ -202,7 +203,7 @@ void ShaderManagerVulkan::DirtyShader() { DirtyLastShader(); } -void ShaderManagerVulkan::DirtyLastShader() { // disables vertex arrays +void ShaderManagerVulkan::DirtyLastShader() { lastVShader_ = nullptr; lastFShader_ = nullptr; gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); @@ -243,6 +244,7 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_) { *vshader = lastVShader_; *fshader = lastFShader_; + _dbg_assert_msg_(G3D, (*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was cached"); // Already all set, no need to look up in shader maps. return; } @@ -272,6 +274,7 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader *vshader = vs; *fshader = fs; + _dbg_assert_msg_(G3D, (*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was computed"); } std::vector ShaderManagerVulkan::DebugGetShaderIDs(DebugShaderType type) { diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index d6e92a9e56..9d26eeb4d2 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -672,7 +672,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, bool replaceIm int size = stride * mipHeight; uint32_t bufferOffset; VkBuffer texBuf; - void *data = drawEngine_->GetPushBufferForTextureData()->Push(size, &bufferOffset, &texBuf); + // nvidia returns 1 but that can't be healthy... let's align by 16 as a minimum. + int pushAlignment = std::max(16, (int)vulkan_->GetPhysicalDeviceProperties().limits.optimalBufferCopyOffsetAlignment); + void *data = drawEngine_->GetPushBufferForTextureData()->PushAligned(size, &bufferOffset, &texBuf, pushAlignment); if (replaced.Valid()) { replaced.Load(i, data, stride); } else { diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index c43039a5e2..dfd6e9e876 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -399,7 +399,9 @@ void SystemInfoScreen::CreateViews() { deviceSpecs->Add(new ItemHeader("OS Information")); deviceSpecs->Add(new InfoItem("Memory Page Size", StringFromFormat("%d bytes", GetMemoryProtectPageSize()))); deviceSpecs->Add(new InfoItem("RW/RX exclusive: ", PlatformIsWXExclusive() ? "Yes" : "No")); +#ifdef ANDROID deviceSpecs->Add(new InfoItem("Sustained perf mode: ", System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE) ? "Yes" : "No")); +#endif const char *build = "Release"; #ifdef _DEBUG @@ -899,14 +901,17 @@ UI::EventReturn JitCompareScreen::OnCurrentBlock(UI::EventParams &e) { return UI::EVENT_DONE; } -void ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout *view) { +int ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout *view) { using namespace UI; std::vector shaderIds_ = gpu->DebugGetShaderIDs(shaderType); + int count = 0; for (auto id : shaderIds_) { Choice *choice = view->Add(new Choice(gpu->DebugGetShaderString(id, shaderType, SHADER_STRING_SHORT_DESC))); choice->SetTag(id); choice->OnClick.Handle(this, &ShaderListScreen::OnShaderClick); + count++; } + return count; } struct { DebugShaderType type; const char *name; } shaderTypes[] = { @@ -933,9 +938,9 @@ void ShaderListScreen::CreateViews() { for (size_t i = 0; i < ARRAY_SIZE(shaderTypes); i++) { ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0)); LinearLayout *shaderList = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); - ListShaders(shaderTypes[i].type, shaderList); + int count = ListShaders(shaderTypes[i].type, shaderList); scroll->Add(shaderList); - tabs_->AddTab(shaderTypes[i].name, scroll); + tabs_->AddTab(StringFromFormat("%s (%d)", shaderTypes[i].name, count), scroll); } } diff --git a/UI/DevScreens.h b/UI/DevScreens.h index 91cfde222f..6c37850c8b 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -152,7 +152,7 @@ public: void CreateViews() override; private: - void ListShaders(DebugShaderType shaderType, UI::LinearLayout *view); + int ListShaders(DebugShaderType shaderType, UI::LinearLayout *view); UI::EventReturn OnShaderClick(UI::EventParams &e); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 35e8f9576c..ed8106db50 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -79,7 +79,7 @@ #endif #ifndef MOBILE_DEVICE -AVIDump avi; +static AVIDump avi; #endif static bool frameStep_; @@ -320,6 +320,7 @@ void EmuScreen::sendMessage(const char *message, const char *value) { // We will push MainScreen in update(). PSP_Shutdown(); bootPending_ = false; + stopRender_ = true; invalid_ = true; host->UpdateDisassembly(); } else if (!strcmp(message, "reset")) { @@ -331,6 +332,7 @@ void EmuScreen::sendMessage(const char *message, const char *value) { std::string resetError; if (!PSP_InitStart(PSP_CoreParameter(), &resetError)) { ELOG("Error resetting: %s", resetError.c_str()); + stopRender_ = true; screenManager()->switchScreen(new MainScreen()); System_SendMessage("event", "failstartgame"); return; @@ -1019,7 +1021,7 @@ void EmuScreen::postRender() { Draw::DrawContext *draw = screenManager()->getDrawContext(); if (!draw) return; - if (invalid_) + if (stopRender_) draw->WipeQueue(); draw->EndFrame(); } diff --git a/UI/EmuScreen.h b/UI/EmuScreen.h index 3f103513b7..8fefb5226c 100644 --- a/UI/EmuScreen.h +++ b/UI/EmuScreen.h @@ -77,6 +77,7 @@ private: // Something invalid was loaded, don't try to emulate bool invalid_; bool quit_; + bool stopRender_ = false; std::string errorMessage_; // If set, pauses at the end of the frame. diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 6e140984b7..4501eda9e4 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -807,6 +807,7 @@ void GameSettingsScreen::CreateViews() { systemSettings->Add(new CheckBox(&g_Config.bDumpFrames, sy->T("Record Display"))); systemSettings->Add(new CheckBox(&g_Config.bUseFFV1, sy->T("Use Lossless Video Codec (FFV1)"))); systemSettings->Add(new CheckBox(&g_Config.bDumpAudio, sy->T("Record Audio"))); + systemSettings->Add(new CheckBox(&g_Config.bSaveLoadResetsAVdumping, sy->T("Reset Recording on Save/Load State"))); #endif if (!g_Config.bSimpleUI) { systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, sy->T("Day Light Saving"))); diff --git a/ext/native/thin3d/VulkanRenderManager.cpp b/ext/native/thin3d/VulkanRenderManager.cpp index 4f265e8f81..36ca452f21 100644 --- a/ext/native/thin3d/VulkanRenderManager.cpp +++ b/ext/native/thin3d/VulkanRenderManager.cpp @@ -11,7 +11,7 @@ #define VLOG(...) #endif -// TODO: Using a thread here is unfinished and does not work correctly. +// This works great. Not much reason to disable so let's not even bother with an option. const bool useThread = true; #ifndef UINT64_MAX @@ -227,6 +227,9 @@ void VulkanRenderManager::StopThread() { Crash(); } frameData.readyForRun = false; + for (size_t i = 0; i < frameData.steps.size(); i++) { + delete frameData.steps[i]; + } frameData.steps.clear(); std::unique_lock lock(frameData.push_mutex); @@ -803,8 +806,7 @@ void VulkanRenderManager::EndSubmitFrame(int frame) { present.waitSemaphoreCount = 1; VkResult res = vkQueuePresentKHR(vulkan_->GetGraphicsQueue(), &present); - // TODO: Deal with the VK_SUBOPTIMAL_WSI and VK_ERROR_OUT_OF_DATE_WSI - // return codes + // TODO: Deal with VK_SUBOPTIMAL_WSI ? if (res == VK_ERROR_OUT_OF_DATE_KHR) { // ignore, it'll be fine. this happens sometimes during resizes, and we do make sure to recreate the swap chain. } else {