void ShaderCache::HandleGSUIDChange(GeometryShaderUid gs_uid, u32 gs_primitive_type) { s_last_geometry_shader_uid = gs_uid; if (gs_uid.GetUidData()->IsPassthrough()) { s_last_geometry_shader_bytecode = {}; return; } auto gs_iterator = s_gs_bytecode_cache.find(gs_uid); if (gs_iterator != s_gs_bytecode_cache.end()) { s_last_geometry_shader_bytecode = gs_iterator->second; } else { ShaderCode gs_code = GenerateGeometryShaderCode(APIType::D3D, gs_uid.GetUidData()); ID3DBlob* gs_bytecode = nullptr; if (!D3D::CompileGeometryShader(gs_code.GetBuffer(), &gs_bytecode)) { GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); return; } s_last_geometry_shader_bytecode = InsertByteCode(gs_uid, &s_gs_bytecode_cache, gs_bytecode); s_gs_disk_cache.Append(gs_uid, reinterpret_cast<u8*>(gs_bytecode->GetBufferPointer()), static_cast<u32>(gs_bytecode->GetBufferSize())); } }
bool GeometryShaderCache::SetShader(u32 primitive_type) { GeometryShaderUid uid = GetGeometryShaderUid(primitive_type); // Check if the shader is already set if (last_entry) { if (uid == last_uid) { GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); return true; } } last_uid = uid; // Check if the shader is a pass-through shader if (uid.GetUidData()->IsPassthrough()) { // Return the default pass-through shader last_entry = &pass_entry; return true; } // Check if the shader is already in the cache GSCache::iterator iter; iter = GeometryShaders.find(uid); if (iter != GeometryShaders.end()) { const GSCacheEntry& entry = iter->second; last_entry = &entry; return (entry.shader != nullptr); } // Need to compile a new shader ShaderCode code = GenerateGeometryShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); D3DBlob* pbytecode; if (!D3D::CompileGeometryShader(code.GetBuffer(), &pbytecode)) { GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); return false; } // Insert the bytecode into the caches g_gs_disk_cache.Append(uid, pbytecode->Data(), pbytecode->Size()); bool success = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size()); pbytecode->Release(); return success; }
void GeometryShaderCache::PrepareShader( u32 primitive_type, const XFMemory &xfr, const u32 components, bool ongputhread) { GeometryShaderUid uid; GetGeometryShaderUid(uid, primitive_type, xfr, components); if (ongputhread) { s_compiler->ProcCompilationResults(); // Check if the shader is already set if (s_last_entry) { if (uid == s_last_uid) { return; } } s_last_uid = uid; // Check if the shader is a pass-through shader if (uid.GetUidData().IsPassthrough()) { // Return the default pass-through shader s_last_entry = &s_pass_entry; return; } GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); } else { if (s_external_last_uid == uid) { return; } s_external_last_uid = uid; } CompileGShader(uid, ongputhread); }
void ShaderCache::HandleGSUIDChange( const GeometryShaderUid &gs_uid, bool on_gpu_thread) { if (gs_uid.GetUidData().IsPassthrough()) { s_last_geometry_shader_bytecode = &s_pass_entry; return; } s_shaders_lock.lock(); ByteCodeCacheEntry* entry = &gs_bytecode_cache->GetOrAdd(gs_uid); s_shaders_lock.unlock(); if (on_gpu_thread) { s_last_geometry_shader_bytecode = entry; } if (entry->m_initialized.test_and_set()) { return; } // Need to compile a new shader ShaderCompilerWorkUnit *wunit = s_compiler->NewUnit(GEOMETRYSHADERGEN_BUFFERSIZE); wunit->GenerateCodeHandler = [gs_uid](ShaderCompilerWorkUnit* wunit) { ShaderCode code; code.SetBuffer(wunit->code.data()); GenerateGeometryShaderCode(code, gs_uid.GetUidData(), API_D3D11); wunit->codesize = (u32)code.BufferSize(); }; wunit->entrypoint = "main"; wunit->flags = D3DCOMPILE_SKIP_VALIDATION | D3DCOMPILE_OPTIMIZATION_LEVEL3; wunit->target = D3D::GeometryShaderVersionString(); wunit->ResultHandler = [gs_uid, entry](ShaderCompilerWorkUnit* wunit) { if (SUCCEEDED(wunit->cresult)) { D3DBlob* shaderBuffer = new D3DBlob(wunit->shaderbytecode); s_gs_disk_cache.Append(gs_uid, shaderBuffer->Data(), shaderBuffer->Size()); PushByteCode(entry, shaderBuffer); wunit->shaderbytecode->Release(); wunit->shaderbytecode = nullptr; SETSTAT(stats.numGeometryShadersAlive, static_cast<int>(ps_bytecode_cache->size())); INCSTAT(stats.numGeometryShadersCreated); } else { static int num_failures = 0; std::string filename = StringFromFormat("%sbad_gs_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); std::ofstream file; OpenFStream(file, filename, std::ios_base::out); file << ((const char *)wunit->code.data()); file << ((const char *)wunit->error->GetBufferPointer()); file.close(); PanicAlert("Failed to compile geometry shader!\nThis usually happens when trying to use Dolphin with an outdated GPU or integrated GPU like the Intel GMA series.\n\nIf you're sure this is Dolphin's error anyway, post the contents of %s along with this error message at the forums.\n\nDebug info (%s):\n%s", filename, D3D::GeometryShaderVersionString(), (char*)wunit->error->GetBufferPointer()); } }; s_compiler->CompileShaderAsync(wunit); }
void ShaderCache::Init() { s_compiler = &HLSLAsyncCompiler::getInstance(); s_shaders_lock.unlock(); s_pass_entry.m_compiled = true; s_pass_entry.m_initialized.test_and_set(); // This class intentionally shares its shader cache files with DX11, as the shaders are (right now) identical. // Reduces unnecessary compilation when switching between APIs. s_last_domain_shader_bytecode = &s_pass_entry; s_last_hull_shader_bytecode = &s_pass_entry; s_last_geometry_shader_bytecode = &s_pass_entry; s_last_pixel_shader_bytecode = nullptr; s_last_vertex_shader_bytecode = nullptr; s_last_geometry_shader_uid = {}; s_last_pixel_shader_uid = {}; s_last_vertex_shader_uid = {}; s_last_tessellation_shader_uid = {}; s_last_cpu_geometry_shader_uid = {}; s_last_cpu_pixel_shader_uid = {}; s_last_cpu_vertex_shader_uid = {}; s_last_cpu_tessellation_shader_uid = {}; // Ensure shader cache directory exists.. std::string shader_cache_path = File::GetUserPath(D_SHADERCACHE_IDX); if (!File::Exists(shader_cache_path)) File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); std::string title_unique_id = SConfig::GetInstance().m_strGameID; std::string ds_cache_filename = StringFromFormat("%sIDX11-%s-ds.cache", shader_cache_path.c_str(), title_unique_id.c_str()); std::string hs_cache_filename = StringFromFormat("%sIDX11-%s-hs.cache", shader_cache_path.c_str(), title_unique_id.c_str()); std::string gs_cache_filename = StringFromFormat("%sIDX11-%s-gs.cache", shader_cache_path.c_str(), title_unique_id.c_str()); std::string ps_cache_filename = StringFromFormat("%sIDX11-%s-ps.cache", shader_cache_path.c_str(), title_unique_id.c_str()); std::string vs_cache_filename = StringFromFormat("%sIDX11-%s-vs.cache", shader_cache_path.c_str(), title_unique_id.c_str()); pKey_t gameid = (pKey_t)GetMurmurHash3(reinterpret_cast<const u8*>(SConfig::GetInstance().m_strGameID.data()), (u32)SConfig::GetInstance().m_strGameID.size(), 0); vs_bytecode_cache = VsBytecodeCache::Create( gameid, VERTEXSHADERGEN_UID_VERSION, "Ishiiruka.vs", StringFromFormat("%s.vs", title_unique_id.c_str()) ); ps_bytecode_cache = PsBytecodeCache::Create( gameid, PIXELSHADERGEN_UID_VERSION, "Ishiiruka.ps", StringFromFormat("%s.ps", title_unique_id.c_str()) ); gs_bytecode_cache = GsBytecodeCache::Create( gameid, GEOMETRYSHADERGEN_UID_VERSION, "Ishiiruka.gs", StringFromFormat("%s.gs", title_unique_id.c_str()) ); ts_bytecode_cache = TsBytecodeCache::Create( gameid, TESSELLATIONSHADERGEN_UID_VERSION, "Ishiiruka.ts", StringFromFormat("%s.ts", title_unique_id.c_str()) ); DShaderCacheInserter ds_inserter; s_ds_disk_cache.OpenAndRead(ds_cache_filename, ds_inserter); HShaderCacheInserter hs_inserter; s_hs_disk_cache.OpenAndRead(hs_cache_filename, hs_inserter); ShaderCacheInserter<GeometryShaderUid, GsBytecodeCache, &gs_bytecode_cache> gs_inserter; s_gs_disk_cache.OpenAndRead(gs_cache_filename, gs_inserter); ShaderCacheInserter<PixelShaderUid, PsBytecodeCache, &ps_bytecode_cache> ps_inserter; s_ps_disk_cache.OpenAndRead(ps_cache_filename, ps_inserter); ShaderCacheInserter<VertexShaderUid, VsBytecodeCache, &vs_bytecode_cache> vs_inserter; s_vs_disk_cache.OpenAndRead(vs_cache_filename, vs_inserter); // Clear out disk cache when debugging shaders to ensure stale ones don't stick around.. SETSTAT(stats.numGeometryShadersAlive, static_cast<int>(gs_bytecode_cache->size())); SETSTAT(stats.numGeometryShadersCreated, 0); SETSTAT(stats.numPixelShadersAlive, static_cast<int>(ps_bytecode_cache->size())); SETSTAT(stats.numPixelShadersCreated, 0); SETSTAT(stats.numVertexShadersAlive, static_cast<int>(vs_bytecode_cache->size())); SETSTAT(stats.numVertexShadersCreated, 0); if (g_ActiveConfig.bCompileShaderOnStartup) { size_t shader_count = 0; ps_bytecode_cache->ForEachMostUsedByCategory(gameid, [&](const PixelShaderUid& it, size_t total) { PixelShaderUid item = it; item.ClearHASH(); item.CalculateUIDHash(); HandlePSUIDChange(item, true); shader_count++; if ((shader_count & 7) == 0) { Host_UpdateTitle(StringFromFormat("Compiling Pixel Shaders %i %% (%i/%i)", (shader_count * 100) / total, shader_count, total)); s_compiler->WaitForFinish(); } }, [](ByteCodeCacheEntry& entry) { return !entry.m_shader_bytecode.pShaderBytecode; } , true); shader_count = 0; vs_bytecode_cache->ForEachMostUsedByCategory(gameid, [&](const VertexShaderUid& it, size_t total) { VertexShaderUid item = it; item.ClearHASH(); item.CalculateUIDHash(); HandleVSUIDChange(item, true); shader_count++; if ((shader_count & 31) == 0) { Host_UpdateTitle(StringFromFormat("Compiling Vertex Shaders %i %% (%i/%i)", (shader_count * 100) / total, shader_count, total)); s_compiler->WaitForFinish(); } }, [](ByteCodeCacheEntry& entry) { return !entry.m_shader_bytecode.pShaderBytecode; } , true); shader_count = 0; gs_bytecode_cache->ForEachMostUsedByCategory(gameid, [&](const GeometryShaderUid& it, size_t total) { GeometryShaderUid item = it; item.ClearHASH(); item.CalculateUIDHash(); HandleGSUIDChange(item, true); shader_count++; if ((shader_count & 7) == 0) { Host_UpdateTitle(StringFromFormat("Compiling Geometry Shaders %i %% (%i/%i)", (shader_count * 100) / total, shader_count, total)); s_compiler->WaitForFinish(); } }, [](ByteCodeCacheEntry& entry) { return !entry.m_shader_bytecode.pShaderBytecode; } , true); shader_count = 0; ts_bytecode_cache->ForEachMostUsedByCategory(gameid, [&](const TessellationShaderUid& it, size_t total) { TessellationShaderUid item = it; item.ClearHASH(); item.CalculateUIDHash(); HandleTSUIDChange(item, true); shader_count++; if ((shader_count & 31) == 0) { Host_UpdateTitle(StringFromFormat("Compiling Tessellation Shaders %i %% (%i/%i)", (shader_count * 100) / total, shader_count, total)); s_compiler->WaitForFinish(); } }, [](std::pair<ByteCodeCacheEntry, ByteCodeCacheEntry>& entry) { return !entry.first.m_shader_bytecode.pShaderBytecode; } , true); s_compiler->WaitForFinish(); } }
bool GeometryShaderCache::SetShader(u32 primitive_type) { switch (primitive_type) { case PRIMITIVE_TRIANGLES: currentPrimitiveTopology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; break; case PRIMITIVE_LINES: currentPrimitiveTopology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; break; case PRIMITIVE_POINTS: currentPrimitiveTopology = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; break; default: CHECK(0, "Invalid primitive type."); break; } GeometryShaderUid uid = GetGeometryShaderUid(primitive_type, API_D3D); // Check if the shader is already set if (uid == last_uid) { GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE,true); return true; } last_uid = uid; D3D::commandListMgr->dirtyPso = true; if (g_ActiveConfig.bEnableShaderDebugging) { ShaderCode code = GenerateGeometryShaderCode(primitive_type, API_D3D); geometry_uid_checker.AddToIndexAndCheck(code, uid, "Geometry", "g"); } // Check if the shader is a pass-through shader if (uid.GetUidData()->IsPassthrough()) { // Return the default pass-through shader last_entry = &pass_entry; return true; } // Check if the shader is already in the cache GSCache::iterator iter; iter = GeometryShaders.find(uid); if (iter != GeometryShaders.end()) { const GSCacheEntry &entry = iter->second; last_entry = &entry; return (entry.shader12.pShaderBytecode != nullptr); } // Need to compile a new shader ShaderCode code = GenerateGeometryShaderCode(primitive_type, API_D3D); D3DBlob* pbytecode; if (!D3D::CompileGeometryShader(code.GetBuffer(), &pbytecode)) { GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); return false; } // Insert the bytecode into the caches g_gs_disk_cache.Append(uid, pbytecode->Data(), pbytecode->Size()); bool success = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size()); pbytecode->Release(); if (g_ActiveConfig.bEnableShaderDebugging && success) { GeometryShaders[uid].code = code.GetBuffer(); } return success; }
void GeometryShaderCache::CompileGShader(const GeometryShaderUid& uid, bool ongputhread) { s_geometry_shaders_lock.lock(); GSCacheEntry* entry = &s_geometry_shaders->GetOrAdd(uid); s_geometry_shaders_lock.unlock(); if (ongputhread) { s_last_entry = entry; } // Compile only when we have a new instance if (entry->initialized.test_and_set()) { return; } // Need to compile a new shader ShaderCompilerWorkUnit *wunit = s_compiler->NewUnit(GEOMETRYSHADERGEN_BUFFERSIZE); wunit->GenerateCodeHandler = [uid](ShaderCompilerWorkUnit* wunit) { ShaderCode code; code.SetBuffer(wunit->code.data()); GenerateGeometryShaderCode(code, uid.GetUidData(), API_D3D11); wunit->codesize = (u32)code.BufferSize(); }; wunit->entrypoint = "main"; #if defined(_DEBUG) || defined(DEBUGFAST) wunit->flags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #else wunit->flags = D3DCOMPILE_SKIP_VALIDATION | D3DCOMPILE_OPTIMIZATION_LEVEL3; #endif wunit->target = D3D::GeometryShaderVersionString(); wunit->ResultHandler = [uid, entry](ShaderCompilerWorkUnit* wunit) { if (SUCCEEDED(wunit->cresult)) { ID3DBlob* shaderBuffer = wunit->shaderbytecode; const u8* bytecode = (const u8*)shaderBuffer->GetBufferPointer(); u32 bytecodelen = (u32)shaderBuffer->GetBufferSize(); g_gs_disk_cache.Append(uid, bytecode, bytecodelen); PushByteCode(bytecode, bytecodelen, entry); } else { static int num_failures = 0; char szTemp[MAX_PATH]; sprintf(szTemp, "%sbad_gs_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), num_failures++); std::ofstream file; OpenFStream(file, szTemp, std::ios_base::out); file << ((const char *)wunit->code.data()); file << ((const char *)wunit->error->GetBufferPointer()); file.close(); PanicAlert("Failed to compile geometry shader!\nThis usually happens when trying to use Dolphin with an outdated GPU or integrated GPU like the Intel GMA series.\n\nIf you're sure this is Dolphin's error anyway, post the contents of %s along with this error message at the forums.\n\nDebug info (%s):\n%s", szTemp, D3D::GeometryShaderVersionString(), (char*)wunit->error->GetBufferPointer()); } }; s_compiler->CompileShaderAsync(wunit); }
)hlsl"; void GeometryShaderCache::Init() { s_compiler = &HLSLAsyncCompiler::getInstance(); s_geometry_shaders_lock.unlock(); bool use_partial_buffer_update = D3D::SupportPartialContantBufferUpdate(); u32 gbsize = static_cast<u32>(Common::AlignUpSizePow2(sizeof(GeometryShaderConstants), 16) * (use_partial_buffer_update ? 1024 : 1)); // must be a multiple of 16 gscbuf = new D3D::ConstantStreamBuffer(gbsize); ID3D11Buffer* buf = gscbuf->GetBuffer(); CHECK(buf != nullptr, "Create geometry shader constant buffer (size=%u)", gbsize); D3D::SetDebugObjectName(buf, "geometry shader constant buffer used to emulate the GX pipeline"); // used when drawing clear quads ClearGeometryShader = D3D::CompileAndCreateGeometryShader(gs_clear_shader_code); CHECK(ClearGeometryShader != nullptr, "Create clear geometry shader"); D3D::SetDebugObjectName(ClearGeometryShader.get(), "clear geometry shader"); // used for buffer copy CopyGeometryShader = D3D::CompileAndCreateGeometryShader(gs_copy_shader_code); CHECK(CopyGeometryShader != nullptr, "Create copy geometry shader"); D3D::SetDebugObjectName(CopyGeometryShader.get(), "copy geometry shader"); Clear(); if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); pKey_t gameid = (pKey_t)GetMurmurHash3(reinterpret_cast<const u8*>(SConfig::GetInstance().m_strGameID.data()), (u32)SConfig::GetInstance().m_strGameID.size(), 0); s_geometry_shaders = GSCache::Create( gameid, GEOMETRYSHADERGEN_UID_VERSION, "Ishiiruka.gs", StringFromFormat("%s.gs", SConfig::GetInstance().m_strGameID.c_str()) ); std::string cache_filename = StringFromFormat("%sIDX11-%s-gs.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(), SConfig::GetInstance().m_strGameID.c_str()); GeometryShaderCacheInserter inserter; g_gs_disk_cache.OpenAndRead(cache_filename, inserter); if (g_ActiveConfig.bCompileShaderOnStartup) { size_t shader_count = 0; s_geometry_shaders->ForEachMostUsedByCategory(gameid, [&](const GeometryShaderUid& it, size_t total) { GeometryShaderUid item = it; item.ClearHASH(); item.CalculateUIDHash(); CompileGShader(item, true); shader_count++; if ((shader_count & 7) == 0) { Host_UpdateTitle(StringFromFormat("Compiling Geometry Shaders %i %% (%i/%i)", (shader_count * 100) / total, shader_count, total)); s_compiler->WaitForFinish(); } }, [](GSCacheEntry& entry) { return !entry.shader; } , true); s_compiler->WaitForFinish(); } s_last_entry = nullptr; }
bool GeometryShaderCache::SetShader(u32 primitive_type) { GeometryShaderUid uid; GetGeometryShaderUid(uid, primitive_type, API_D3D); if (g_ActiveConfig.bEnableShaderDebugging) { ShaderCode code; GenerateGeometryShaderCode(code, primitive_type, API_D3D); geometry_uid_checker.AddToIndexAndCheck(code, uid, "Geometry", "g"); } // Check if the shader is already set if (last_entry) { if (uid == last_uid) { GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE,true); return true; } } last_uid = uid; // Check if the shader is a pass-through shader if (uid.GetUidData()->IsPassthrough()) { // Return the default pass-through shader last_entry = &pass_entry; return true; } // Check if the shader is already in the cache GSCache::iterator iter; iter = GeometryShaders.find(uid); if (iter != GeometryShaders.end()) { const GSCacheEntry &entry = iter->second; last_entry = &entry; return (entry.shader != nullptr); } // Need to compile a new shader ShaderCode code; GenerateGeometryShaderCode(code, primitive_type, API_D3D); D3DBlob* pbytecode; if (!D3D::CompileGeometryShader(code.GetBuffer(), &pbytecode)) { GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); return false; } // Insert the bytecode into the caches g_gs_disk_cache.Append(uid, pbytecode->Data(), pbytecode->Size()); bool success = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size()); pbytecode->Release(); if (g_ActiveConfig.bEnableShaderDebugging && success) { GeometryShaders[uid].code = code.GetBuffer(); } return success; }