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(); } }
void PixelShaderCache::Init() { s_pixel_shaders_lock.unlock(); for (u32 i = 0; i < PSRM_DEPTH_ONLY + 1; i++) { s_last_entry[i] = nullptr; } s_compiler = &HLSLAsyncCompiler::getInstance(); //program used for clear screen { char pprog[3072]; sprintf(pprog, "void main(\n" "out float4 ocol0 : COLOR0,\n" " in float4 incol0 : COLOR0){\n" "ocol0 = incol0;\n" "}\n"); s_clear_program = D3D::CompileAndCreatePixelShader(pprog, (int)strlen(pprog)); } int shaderModel = ((D3D::GetCaps().PixelShaderVersion >> 8) & 0xFF); int maxConstants = (shaderModel < 3) ? 32 : ((shaderModel < 4) ? 224 : 65536); // other screen copy/convert programs for (int copyMatrixType = 0; copyMatrixType < NUM_COPY_TYPES; copyMatrixType++) { for (int depthType = 0; depthType < NUM_DEPTH_CONVERSION_TYPES; depthType++) { for (int ssaaMode = 0; ssaaMode < MAX_SSAA_SHADERS; ssaaMode++) { if (ssaaMode && !s_copy_program[copyMatrixType][depthType][ssaaMode - 1] || depthType && !s_copy_program[copyMatrixType][depthType - 1][ssaaMode] || copyMatrixType && !s_copy_program[copyMatrixType - 1][depthType][ssaaMode]) { // if it failed at a lower setting, it's going to fail here for the same reason it did there, // so skip this attempt to avoid duplicate error messages. s_copy_program[copyMatrixType][depthType][ssaaMode] = NULL; } else { s_copy_program[copyMatrixType][depthType][ssaaMode] = CreateCopyShader(copyMatrixType, depthType, ssaaMode); } } } } Clear(); if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX).c_str()); SETSTAT(stats.numPixelShadersCreated, 0); SETSTAT(stats.numPixelShadersAlive, 0); pKey_t gameid = (pKey_t)GetMurmurHash3(reinterpret_cast<const u8*>(SConfig::GetInstance().m_strGameID.data()), (u32)SConfig::GetInstance().m_strGameID.size(), 0); s_pshaders = ObjectUsageProfiler<PixelShaderUid, pKey_t, PSCacheEntry, PixelShaderUid::ShaderUidHasher>::Create( gameid, PIXELSHADERGEN_UID_VERSION, "Ishiiruka.ps.dx9", StringFromFormat("%s.ps.dx9", SConfig::GetInstance().m_strGameID.c_str()) ); std::string cache_filename = StringFromFormat("%sIDX9-%s-ps.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(), SConfig::GetInstance().m_strGameID.c_str()); PixelShaderCacheInserter inserter; g_ps_disk_cache.OpenAndRead(cache_filename, inserter); if (g_ActiveConfig.bCompileShaderOnStartup) { std::vector<PixelShaderUid> shaders; size_t shader_count = 0; s_pshaders->ForEachMostUsedByCategory(gameid, [&](const PixelShaderUid& item, size_t total) { PixelShaderUid newitem = item; pixel_shader_uid_data& uid_data = newitem.GetUidData<pixel_shader_uid_data>(); if (uid_data.render_mode == PSRM_DUAL_SOURCE_BLEND && !g_ActiveConfig.backend_info.bSupportsDualSourceBlend) { uid_data.render_mode = PSRM_DEFAULT; newitem.ClearHASH(); newitem.CalculateUIDHash(); CompilePShader(newitem, PIXEL_SHADER_RENDER_MODE::PSRM_DEFAULT, true); uid_data.render_mode = PSRM_ALPHA_PASS; uid_data.fog_proj = 0; uid_data.fog_RangeBaseEnabled = 0; newitem.ClearHASH(); newitem.CalculateUIDHash(); CompilePShader(newitem, PIXEL_SHADER_RENDER_MODE::PSRM_ALPHA_PASS, true); } else { newitem.ClearHASH(); newitem.CalculateUIDHash(); CompilePShader(newitem, PIXEL_SHADER_RENDER_MODE::PSRM_DEFAULT, 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(); } }, [](PSCacheEntry& entry) { return !entry.shader; }, true); s_compiler->WaitForFinish(); } }
})hlsl"; void VertexShaderCache::Init() { s_vshaders_lock.unlock(); s_compiler = &HLSLAsyncCompiler::getInstance(); const D3D11_INPUT_ELEMENT_DESC simpleelems[3] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "TEXCOORD", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 } }; const D3D11_INPUT_ELEMENT_DESC clearelems[2] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; bool use_partial_buffer_update = D3D::SupportPartialContantBufferUpdate(); u32 cbsize = VertexShaderManager::ConstantBufferSize * sizeof(float) * (use_partial_buffer_update ? 1024 : 1); // is always multiple of 16 vscbuf = new D3D::ConstantStreamBuffer(cbsize); ID3D11Buffer* buf = vscbuf->GetBuffer(); CHECK(buf != nullptr, "Create vertex shader constant buffer (size=%u)", cbsize); D3D::SetDebugObjectName(buf, "vertex shader constant buffer used to emulate the GX pipeline"); D3DBlob blob; D3D::CompileShader(D3D::ShaderType::Vertex, simple_shader_code, blob); D3D::device->CreateInputLayout(simpleelems, 3, blob.Data(), blob.Size(), D3D::ToAddr(s_simple_layout)); s_simple_vertex_shader = D3D::CreateVertexShaderFromByteCode(blob); if (s_simple_layout == nullptr || s_simple_vertex_shader == nullptr) PanicAlert("Failed to create simple vertex shader or input layout at %s %d\n", __FILE__, __LINE__); D3D::SetDebugObjectName(s_simple_layout.get(), "simple input layout"); D3D::SetDebugObjectName(s_simple_vertex_shader.get(), "simple vertex shader"); D3D::CompileShader(D3D::ShaderType::Vertex, clear_shader_code, blob); D3D::device->CreateInputLayout(clearelems, 2, blob.Data(), blob.Size(), D3D::ToAddr(s_clear_layout)); s_clear_vertex_shader = D3D::CreateVertexShaderFromByteCode(blob); if (s_clear_layout == nullptr || s_clear_vertex_shader == nullptr) PanicAlert("Failed to create clear vertex shader or input layout at %s %d\n", __FILE__, __LINE__); D3D::SetDebugObjectName(s_clear_vertex_shader.get(), "clear vertex shader"); D3D::SetDebugObjectName(s_clear_layout.get(), "clear input layout"); Clear(); if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX).c_str()); SETSTAT(stats.numVertexShadersCreated, 0); SETSTAT(stats.numVertexShadersAlive, 0); pKey_t gameid = (pKey_t)GetMurmurHash3(reinterpret_cast<const u8*>(SConfig::GetInstance().m_strGameID.data()), (u32)SConfig::GetInstance().m_strGameID.size(), 0); s_vshaders = VSCache::Create( gameid, VERTEXSHADERGEN_UID_VERSION, "Ishiiruka.vs", StringFromFormat("%s.vs", SConfig::GetInstance().m_strGameID.c_str()) ); std::string cache_filename = StringFromFormat("%sIDX11-%s-vs.cache", File::GetUserPath(D_SHADERCACHE_IDX).c_str(), SConfig::GetInstance().m_strGameID.c_str()); VertexShaderCacheInserter inserter; g_vs_disk_cache.OpenAndRead(cache_filename, inserter); if (g_ActiveConfig.bCompileShaderOnStartup) { size_t shader_count = 0; s_vshaders->ForEachMostUsedByCategory(gameid, [&](const VertexShaderUid& it, size_t total) { VertexShaderUid item = it; item.ClearHASH(); item.CalculateUIDHash(); CompileVShader(item, true); shader_count++; if ((shader_count & 7) == 0) { Host_UpdateTitle(StringFromFormat("Compiling Vertex Shaders %i %% (%i/%i)", (shader_count * 100) / total, shader_count, total)); s_compiler->WaitForFinish(); } }, [](VSCacheEntry& entry) { return !entry.shader; } , true); s_compiler->WaitForFinish(); } s_last_entry = nullptr; }
)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; }