mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-01-15 08:23:17 -03:00
VideoCommon: update resource manager with a material/shader/and texture(+sampler) resource to show the complexities that warrant the resource manager system
This commit is contained in:
@@ -756,7 +756,10 @@
|
||||
<ClInclude Include="VideoCommon\RenderState.h" />
|
||||
<ClInclude Include="VideoCommon\Resources\CustomResourceManager.h" />
|
||||
<ClInclude Include="VideoCommon\Resources\InvalidTextures.h" />
|
||||
<ClInclude Include="VideoCommon\Resources\MaterialResource.h" />
|
||||
<ClInclude Include="VideoCommon\Resources\Resource.h" />
|
||||
<ClInclude Include="VideoCommon\Resources\ShaderResource.h" />
|
||||
<ClInclude Include="VideoCommon\Resources\TextureAndSampler.h" />
|
||||
<ClInclude Include="VideoCommon\Resources\TextureDataResource.h" />
|
||||
<ClInclude Include="VideoCommon\Resources\TexturePool.h" />
|
||||
<ClInclude Include="VideoCommon\ShaderCache.h" />
|
||||
@@ -1412,7 +1415,10 @@
|
||||
<ClCompile Include="VideoCommon\RenderState.cpp" />
|
||||
<ClCompile Include="VideoCommon\Resources\CustomResourceManager.cpp" />
|
||||
<ClCompile Include="VideoCommon\Resources\InvalidTextures.cpp" />
|
||||
<ClCompile Include="VideoCommon\Resources\MaterialResource.cpp" />
|
||||
<ClCompile Include="VideoCommon\Resources\Resource.cpp" />
|
||||
<ClCompile Include="VideoCommon\Resources\ShaderResource.cpp" />
|
||||
<ClCompile Include="VideoCommon\Resources\TextureAndSamplerResource.cpp" />
|
||||
<ClCompile Include="VideoCommon\Resources\TextureDataResource.cpp" />
|
||||
<ClCompile Include="VideoCommon\Resources\TexturePool.cpp" />
|
||||
<ClCompile Include="VideoCommon\ShaderCache.cpp" />
|
||||
|
||||
@@ -152,8 +152,14 @@ add_library(videocommon
|
||||
Resources/CustomResourceManager.h
|
||||
Resources/InvalidTextures.cpp
|
||||
Resources/InvalidTextures.h
|
||||
Resources/MaterialResource.cpp
|
||||
Resources/MaterialResource.h
|
||||
Resources/Resource.cpp
|
||||
Resources/Resource.h
|
||||
Resources/ShaderResource.cpp
|
||||
Resources/ShaderResource.h
|
||||
Resources/TextureAndSamplerResource.cpp
|
||||
Resources/TextureAndSamplerResource.h
|
||||
Resources/TextureDataResource.cpp
|
||||
Resources/TextureDataResource.h
|
||||
Resources/TexturePool.cpp
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
|
||||
#include "VideoCommon/Resources/CustomResourceManager.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/PipelineUtils.h"
|
||||
#include "VideoCommon/Resources/InvalidTextures.h"
|
||||
#include "VideoCommon/VideoEvents.h"
|
||||
|
||||
namespace VideoCommon
|
||||
@@ -10,22 +15,46 @@ namespace VideoCommon
|
||||
void CustomResourceManager::Initialize()
|
||||
{
|
||||
m_asset_cache.Initialize();
|
||||
m_worker_thread.Reset("resource-worker");
|
||||
m_host_config.bits = ShaderHostConfig::GetCurrent().bits;
|
||||
m_async_shader_compiler = g_gfx->CreateAsyncShaderCompiler();
|
||||
|
||||
m_async_shader_compiler->StartWorkerThreads(1); // TODO expose to config
|
||||
|
||||
m_xfb_event =
|
||||
AfterFrameEvent::Register([this](Core::System&) { XFBTriggered(); }, "CustomResourceManager");
|
||||
GetVideoEvents().after_frame_event.Register([this](Core::System&) { XFBTriggered(); });
|
||||
|
||||
m_invalid_array_texture = CreateInvalidArrayTexture();
|
||||
m_invalid_color_texture = CreateInvalidColorTexture();
|
||||
m_invalid_cubemap_texture = CreateInvalidCubemapTexture();
|
||||
m_invalid_transparent_texture = CreateInvalidTransparentTexture();
|
||||
}
|
||||
|
||||
void CustomResourceManager::Shutdown()
|
||||
{
|
||||
if (m_async_shader_compiler)
|
||||
m_async_shader_compiler->StopWorkerThreads();
|
||||
|
||||
m_asset_cache.Shutdown();
|
||||
m_worker_thread.Shutdown();
|
||||
Reset();
|
||||
}
|
||||
|
||||
void CustomResourceManager::Reset()
|
||||
{
|
||||
m_material_resources.clear();
|
||||
m_shader_resources.clear();
|
||||
m_texture_data_resources.clear();
|
||||
m_texture_sampler_resources.clear();
|
||||
|
||||
m_invalid_transparent_texture.reset();
|
||||
m_invalid_color_texture.reset();
|
||||
m_invalid_cubemap_texture.reset();
|
||||
m_invalid_array_texture.reset();
|
||||
|
||||
m_asset_cache.Reset();
|
||||
m_texture_pool.Reset();
|
||||
m_worker_thread.Reset("resource-worker");
|
||||
}
|
||||
|
||||
void CustomResourceManager::MarkAssetDirty(const CustomAssetLibrary::AssetID& asset_id)
|
||||
@@ -38,24 +67,99 @@ void CustomResourceManager::XFBTriggered()
|
||||
m_asset_cache.Update();
|
||||
}
|
||||
|
||||
void CustomResourceManager::SetHostConfig(const ShaderHostConfig& host_config)
|
||||
{
|
||||
for (auto& [id, shader_resources] : m_shader_resources)
|
||||
{
|
||||
for (auto& [key, shader_resource] : shader_resources)
|
||||
{
|
||||
shader_resource->SetHostConfig(host_config);
|
||||
|
||||
// Hack to get access to resource internals
|
||||
Resource* resource = shader_resource.get();
|
||||
|
||||
// Tell shader and references to trigger a reload
|
||||
// on next usage
|
||||
resource->NotifyAssetChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
m_host_config.bits = host_config.bits;
|
||||
}
|
||||
|
||||
TextureDataResource* CustomResourceManager::GetTextureDataFromAsset(
|
||||
const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
|
||||
{
|
||||
const auto [it, added] = m_texture_data_resources.try_emplace(asset_id, nullptr);
|
||||
if (added)
|
||||
auto& resource = m_texture_data_resources[asset_id];
|
||||
if (resource == nullptr)
|
||||
{
|
||||
it->second = std::make_unique<TextureDataResource>(CreateResourceContext(asset_id, library));
|
||||
resource =
|
||||
std::make_unique<TextureDataResource>(CreateResourceContext(asset_id, std::move(library)));
|
||||
}
|
||||
ProcessResource(it->second.get());
|
||||
return it->second.get();
|
||||
ProcessResource(resource.get());
|
||||
return resource.get();
|
||||
}
|
||||
|
||||
MaterialResource* CustomResourceManager::GetMaterialFromAsset(
|
||||
const CustomAssetLibrary::AssetID& asset_id, const GXPipelineUid& pipeline_uid,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
|
||||
{
|
||||
auto& resource = m_material_resources[asset_id][PipelineToHash(pipeline_uid)];
|
||||
if (resource == nullptr)
|
||||
{
|
||||
resource = std::make_unique<MaterialResource>(
|
||||
CreateResourceContext(asset_id, std::move(library)), pipeline_uid);
|
||||
}
|
||||
ProcessResource(resource.get());
|
||||
return resource.get();
|
||||
}
|
||||
|
||||
ShaderResource*
|
||||
CustomResourceManager::GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::size_t shader_key, const GXPipelineUid& pipeline_uid,
|
||||
const std::string& preprocessor_settings,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
|
||||
{
|
||||
auto& resource = m_shader_resources[asset_id][shader_key];
|
||||
if (resource == nullptr)
|
||||
{
|
||||
resource = std::make_unique<ShaderResource>(CreateResourceContext(asset_id, std::move(library)),
|
||||
pipeline_uid, preprocessor_settings, m_host_config);
|
||||
}
|
||||
ProcessResource(resource.get());
|
||||
return resource.get();
|
||||
}
|
||||
|
||||
TextureAndSamplerResource* CustomResourceManager::GetTextureAndSamplerFromAsset(
|
||||
const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
|
||||
{
|
||||
auto& resource = m_texture_sampler_resources[asset_id];
|
||||
if (resource == nullptr)
|
||||
{
|
||||
resource = std::make_unique<TextureAndSamplerResource>(
|
||||
CreateResourceContext(asset_id, std::move(library)));
|
||||
}
|
||||
ProcessResource(resource.get());
|
||||
return resource.get();
|
||||
}
|
||||
|
||||
Resource::ResourceContext CustomResourceManager::CreateResourceContext(
|
||||
const CustomAssetLibrary::AssetID& asset_id,
|
||||
const std::shared_ptr<VideoCommon::CustomAssetLibrary>& library)
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library)
|
||||
{
|
||||
return Resource::ResourceContext{asset_id, library, &m_asset_cache, this};
|
||||
return Resource::ResourceContext{asset_id,
|
||||
std::move(library),
|
||||
&m_asset_cache,
|
||||
this,
|
||||
&m_texture_pool,
|
||||
&m_worker_thread,
|
||||
m_async_shader_compiler.get(),
|
||||
m_invalid_array_texture.get(),
|
||||
m_invalid_color_texture.get(),
|
||||
m_invalid_cubemap_texture.get(),
|
||||
m_invalid_transparent_texture.get()};
|
||||
}
|
||||
|
||||
void CustomResourceManager::ProcessResource(Resource* resource)
|
||||
@@ -71,18 +175,15 @@ void CustomResourceManager::ProcessResource(Resource* resource)
|
||||
return;
|
||||
}
|
||||
|
||||
// Early out if we're already at our end state
|
||||
if (resource->GetState() == Resource::State::DataAvailable)
|
||||
return;
|
||||
|
||||
ProcessResourceState(resource);
|
||||
}
|
||||
|
||||
void CustomResourceManager::ProcessResourceState(Resource* resource)
|
||||
{
|
||||
Resource::State next_state = resource->GetState();
|
||||
const auto current_state = resource->GetState();
|
||||
Resource::State next_state = current_state;
|
||||
Resource::TaskComplete task_complete = Resource::TaskComplete::No;
|
||||
switch (resource->GetState())
|
||||
switch (current_state)
|
||||
{
|
||||
case Resource::State::ReloadData:
|
||||
resource->ResetData();
|
||||
@@ -102,6 +203,14 @@ void CustomResourceManager::ProcessResourceState(Resource* resource)
|
||||
case Resource::State::ProcessingData:
|
||||
task_complete = resource->ProcessData();
|
||||
next_state = Resource::State::DataAvailable;
|
||||
break;
|
||||
case Resource::State::DataAvailable:
|
||||
// Early out, we're already at our end state
|
||||
return;
|
||||
default:
|
||||
ERROR_LOG_FMT(VIDEO, "Unknown resource state '{}' for resource '{}'",
|
||||
static_cast<int>(current_state), resource->m_resource_context.primary_asset_id);
|
||||
return;
|
||||
};
|
||||
|
||||
if (task_complete == Resource::TaskComplete::Yes)
|
||||
|
||||
@@ -7,9 +7,16 @@
|
||||
#include <memory>
|
||||
|
||||
#include "Common/HookableEvent.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
#include "VideoCommon/Assets/CustomAssetCache.h"
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/Resources/MaterialResource.h"
|
||||
#include "VideoCommon/Resources/ShaderResource.h"
|
||||
#include "VideoCommon/Resources/TextureAndSamplerResource.h"
|
||||
#include "VideoCommon/Resources/TextureDataResource.h"
|
||||
#include "VideoCommon/Resources/TexturePool.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
@@ -25,22 +32,53 @@ public:
|
||||
void MarkAssetDirty(const CustomAssetLibrary::AssetID& asset_id);
|
||||
|
||||
void XFBTriggered();
|
||||
void SetHostConfig(const ShaderHostConfig& host_config);
|
||||
|
||||
TextureDataResource*
|
||||
GetTextureDataFromAsset(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
|
||||
MaterialResource* GetMaterialFromAsset(const CustomAssetLibrary::AssetID& asset_id,
|
||||
const GXPipelineUid& pipeline_uid,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
|
||||
|
||||
ShaderResource* GetShaderFromAsset(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::size_t shader_key, const GXPipelineUid& pipeline_uid,
|
||||
const std::string& preprocessor_settings,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
|
||||
TextureAndSamplerResource*
|
||||
GetTextureAndSamplerFromAsset(const CustomAssetLibrary::AssetID& asset_id,
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
|
||||
|
||||
private:
|
||||
Resource::ResourceContext
|
||||
CreateResourceContext(const CustomAssetLibrary::AssetID& asset_id,
|
||||
const std::shared_ptr<VideoCommon::CustomAssetLibrary>& library);
|
||||
std::shared_ptr<VideoCommon::CustomAssetLibrary> library);
|
||||
void ProcessResource(Resource* resource);
|
||||
void ProcessResourceState(Resource* resource);
|
||||
CustomAssetCache m_asset_cache;
|
||||
TexturePool m_texture_pool;
|
||||
Common::AsyncWorkThreadSP m_worker_thread;
|
||||
std::unique_ptr<AsyncShaderCompiler> m_async_shader_compiler;
|
||||
|
||||
using PipelineIdToMaterial = std::map<std::size_t, std::unique_ptr<MaterialResource>>;
|
||||
std::map<CustomAssetLibrary::AssetID, PipelineIdToMaterial> m_material_resources;
|
||||
|
||||
using ShaderKeyToShader = std::map<std::size_t, std::unique_ptr<ShaderResource>>;
|
||||
std::map<CustomAssetLibrary::AssetID, ShaderKeyToShader> m_shader_resources;
|
||||
|
||||
std::map<CustomAssetLibrary::AssetID, std::unique_ptr<TextureDataResource>>
|
||||
m_texture_data_resources;
|
||||
|
||||
std::map<CustomAssetLibrary::AssetID, std::unique_ptr<TextureAndSamplerResource>>
|
||||
m_texture_sampler_resources;
|
||||
|
||||
ShaderHostConfig m_host_config;
|
||||
|
||||
Common::EventHook m_xfb_event;
|
||||
|
||||
std::unique_ptr<AbstractTexture> m_invalid_transparent_texture;
|
||||
std::unique_ptr<AbstractTexture> m_invalid_color_texture;
|
||||
std::unique_ptr<AbstractTexture> m_invalid_cubemap_texture;
|
||||
std::unique_ptr<AbstractTexture> m_invalid_array_texture;
|
||||
};
|
||||
} // namespace VideoCommon
|
||||
|
||||
385
Source/Core/VideoCommon/Resources/MaterialResource.cpp
Normal file
385
Source/Core/VideoCommon/Resources/MaterialResource.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/Resources/MaterialResource.h"
|
||||
|
||||
#include <xxh3.h>
|
||||
|
||||
#include "Common/VariantUtil.h"
|
||||
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/Assets/CustomAssetCache.h"
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/FramebufferManager.h"
|
||||
#include "VideoCommon/PipelineUtils.h"
|
||||
#include "VideoCommon/Resources/CustomResourceManager.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// TODO: absorb this with TextureCacheBase
|
||||
bool IsAnisotropicEnhancementSafe(const SamplerState::TM0& tm0)
|
||||
{
|
||||
return !(tm0.min_filter == FilterMode::Near && tm0.mag_filter == FilterMode::Near);
|
||||
}
|
||||
|
||||
// TODO: absorb this with TextureCacheBase
|
||||
SamplerState CalculateSamplerAnisotropy(const SamplerState& initial_sampler)
|
||||
{
|
||||
SamplerState state = initial_sampler;
|
||||
if (g_ActiveConfig.iMaxAnisotropy != AnisotropicFilteringMode::Default &&
|
||||
IsAnisotropicEnhancementSafe(state.tm0))
|
||||
{
|
||||
state.tm0.anisotropic_filtering = Common::ToUnderlying(g_ActiveConfig.iMaxAnisotropy);
|
||||
}
|
||||
|
||||
if (state.tm0.anisotropic_filtering != 0)
|
||||
{
|
||||
// https://www.opengl.org/registry/specs/EXT/texture_filter_anisotropic.txt
|
||||
// For predictable results on all hardware/drivers, only use one of:
|
||||
// GL_LINEAR + GL_LINEAR (No Mipmaps [Bilinear])
|
||||
// GL_LINEAR + GL_LINEAR_MIPMAP_LINEAR (w/ Mipmaps [Trilinear])
|
||||
// Letting the game set other combinations will have varying arbitrary results;
|
||||
// possibly being interpreted as equal to bilinear/trilinear, implicitly
|
||||
// disabling anisotropy, or changing the anisotropic algorithm employed.
|
||||
state.tm0.min_filter = FilterMode::Linear;
|
||||
state.tm0.mag_filter = FilterMode::Linear;
|
||||
state.tm0.mipmap_filter = FilterMode::Linear;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
MaterialResource::MaterialResource(Resource::ResourceContext resource_context,
|
||||
const GXPipelineUid& pipeline_uid)
|
||||
: Resource(std::move(resource_context)), m_uid(pipeline_uid)
|
||||
{
|
||||
m_material_asset = m_resource_context.asset_cache->CreateAsset<MaterialAsset>(
|
||||
m_resource_context.primary_asset_id, m_resource_context.asset_library, this);
|
||||
m_uid_vertex_format_copy =
|
||||
g_gfx->CreateNativeVertexFormat(m_uid.vertex_format->GetVertexDeclaration());
|
||||
m_uid.vertex_format = m_uid_vertex_format_copy.get();
|
||||
}
|
||||
|
||||
void MaterialResource::ResetData()
|
||||
{
|
||||
if (m_current_data)
|
||||
{
|
||||
m_current_data->m_shader_resource->RemoveReference(this);
|
||||
for (const auto& texture_like_resource : m_current_data->m_texture_like_resources)
|
||||
{
|
||||
if (texture_like_resource)
|
||||
texture_like_resource->RemoveReference(this);
|
||||
}
|
||||
if (m_current_data->m_next_material)
|
||||
m_current_data->m_next_material->RemoveReference(this);
|
||||
}
|
||||
m_load_data = std::make_shared<Data>();
|
||||
m_processing_load_data = false;
|
||||
}
|
||||
|
||||
Resource::TaskComplete MaterialResource::CollectPrimaryData()
|
||||
{
|
||||
const auto material_data = m_material_asset->GetData();
|
||||
if (!material_data) [[unlikely]]
|
||||
{
|
||||
return Resource::TaskComplete::No;
|
||||
}
|
||||
m_load_data->m_material_data = material_data;
|
||||
|
||||
// A shader asset is required to function
|
||||
if (m_load_data->m_material_data->shader_asset == "")
|
||||
{
|
||||
return Resource::TaskComplete::Error;
|
||||
}
|
||||
|
||||
CreateTextureData(m_load_data.get());
|
||||
SetShaderKey(m_load_data.get(), &m_uid);
|
||||
|
||||
return Resource::TaskComplete::Yes;
|
||||
}
|
||||
|
||||
Resource::TaskComplete MaterialResource::CollectDependencyData()
|
||||
{
|
||||
bool loaded = true;
|
||||
{
|
||||
auto* const shader_resource = m_resource_context.resource_manager->GetShaderFromAsset(
|
||||
m_load_data->m_material_data->shader_asset, m_load_data->m_shader_key, m_uid,
|
||||
m_load_data->m_preprocessor_settings, m_resource_context.asset_library);
|
||||
shader_resource->AddReference(this);
|
||||
m_load_data->m_shader_resource = shader_resource;
|
||||
const auto data_processed = shader_resource->IsDataProcessed();
|
||||
if (data_processed == TaskComplete::Error)
|
||||
return TaskComplete::Error;
|
||||
|
||||
loaded &= data_processed == TaskComplete::Yes;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < m_load_data->m_material_data->textures.size(); i++)
|
||||
{
|
||||
const auto& texture_and_sampler = m_load_data->m_material_data->textures[i];
|
||||
if (texture_and_sampler.asset == "")
|
||||
continue;
|
||||
|
||||
const auto texture = m_resource_context.resource_manager->GetTextureAndSamplerFromAsset(
|
||||
texture_and_sampler.asset, m_resource_context.asset_library);
|
||||
m_load_data->m_texture_like_resources[i] = texture;
|
||||
m_load_data->m_texture_like_data[i] = texture->GetData();
|
||||
texture->AddReference(this);
|
||||
|
||||
const auto data_processed = texture->IsDataProcessed();
|
||||
if (data_processed == TaskComplete::Error)
|
||||
return TaskComplete::Error;
|
||||
|
||||
loaded &= data_processed == TaskComplete::Yes;
|
||||
}
|
||||
|
||||
if (m_load_data->m_material_data->next_material_asset != "")
|
||||
{
|
||||
m_load_data->m_next_material = m_resource_context.resource_manager->GetMaterialFromAsset(
|
||||
m_load_data->m_material_data->next_material_asset, m_uid, m_resource_context.asset_library);
|
||||
m_load_data->m_next_material->AddReference(this);
|
||||
const auto data_processed = m_load_data->m_next_material->IsDataProcessed();
|
||||
if (data_processed == TaskComplete::Error)
|
||||
return TaskComplete::Error;
|
||||
|
||||
loaded &= data_processed == TaskComplete::Yes;
|
||||
}
|
||||
|
||||
return loaded ? TaskComplete::Yes : TaskComplete::No;
|
||||
}
|
||||
|
||||
Resource::TaskComplete MaterialResource::ProcessData()
|
||||
{
|
||||
auto shader_data = m_load_data->m_shader_resource->GetData();
|
||||
|
||||
if (!shader_data) [[unlikely]]
|
||||
return Resource::TaskComplete::Error;
|
||||
|
||||
for (std::size_t i = 0; i < m_load_data->m_texture_like_data.size(); i++)
|
||||
{
|
||||
auto& texture_like_reference = m_load_data->m_texture_like_references[i];
|
||||
const auto& texture_and_sampler = m_load_data->m_material_data->textures[i];
|
||||
|
||||
// If the texture doesn't exist, use one of the placeholders
|
||||
if (texture_and_sampler.asset == "")
|
||||
{
|
||||
const auto texture_type = shader_data->GetTextureType(i);
|
||||
if (texture_type == AbstractTextureType::Texture_2D)
|
||||
texture_like_reference.texture = m_resource_context.invalid_color_texture;
|
||||
else if (texture_type == AbstractTextureType::Texture_2DArray)
|
||||
texture_like_reference.texture = m_resource_context.invalid_array_texture;
|
||||
else if (texture_type == AbstractTextureType::Texture_CubeMap)
|
||||
texture_like_reference.texture = m_resource_context.invalid_cubemap_texture;
|
||||
|
||||
if (texture_like_reference.texture == nullptr)
|
||||
{
|
||||
PanicAlertFmt("Invalid texture (texture_type={}) is not found during material "
|
||||
"resource processing (asset_id={})",
|
||||
texture_type, m_resource_context.primary_asset_id);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& texture_like_data = m_load_data->m_texture_like_data[i];
|
||||
|
||||
std::visit(overloaded{[&](const std::shared_ptr<TextureAndSamplerResource::Data>& data) {
|
||||
texture_like_reference.texture = data->GetTexture();
|
||||
texture_like_reference.sampler = CalculateSamplerAnisotropy(data->GetSampler());
|
||||
;
|
||||
}},
|
||||
texture_like_data);
|
||||
}
|
||||
|
||||
class WorkItem final : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
WorkItem(std::shared_ptr<MaterialResource::Data> material_resource_data,
|
||||
std::shared_ptr<ShaderResource::Data> shader_resource_data,
|
||||
VideoCommon::GXPipelineUid* uid, FramebufferState frame_buffer_state)
|
||||
: m_material_resource_data(std::move(material_resource_data)),
|
||||
m_shader_resource_data(std::move(shader_resource_data)), m_uid(uid),
|
||||
m_frame_buffer_state(frame_buffer_state)
|
||||
{
|
||||
}
|
||||
|
||||
bool Compile() override
|
||||
{
|
||||
// Sanity check
|
||||
if (!m_shader_resource_data->IsCompiled())
|
||||
{
|
||||
m_material_resource_data->m_processing_finished = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
AbstractPipelineConfig config;
|
||||
config.vertex_shader = m_shader_resource_data->GetVertexShader();
|
||||
config.pixel_shader = m_shader_resource_data->GetPixelShader();
|
||||
config.geometry_shader = m_shader_resource_data->GetGeometryShader();
|
||||
|
||||
const auto actual_uid = ApplyDriverBugs(*m_uid);
|
||||
|
||||
if (m_material_resource_data->m_material_data->blending_state)
|
||||
config.blending_state = *m_material_resource_data->m_material_data->blending_state;
|
||||
else
|
||||
config.blending_state = actual_uid.blending_state;
|
||||
|
||||
if (m_material_resource_data->m_material_data->depth_state)
|
||||
config.depth_state = *m_material_resource_data->m_material_data->depth_state;
|
||||
else
|
||||
config.depth_state = actual_uid.depth_state;
|
||||
|
||||
config.framebuffer_state = std::move(m_frame_buffer_state);
|
||||
config.framebuffer_state.additional_color_attachment_count = 0;
|
||||
|
||||
config.rasterization_state = actual_uid.rasterization_state;
|
||||
if (m_material_resource_data->m_material_data->cull_mode)
|
||||
{
|
||||
config.rasterization_state.cull_mode =
|
||||
*m_material_resource_data->m_material_data->cull_mode;
|
||||
}
|
||||
|
||||
config.vertex_format = actual_uid.vertex_format;
|
||||
config.usage = AbstractPipelineUsage::GX;
|
||||
|
||||
m_material_resource_data->m_pipeline = g_gfx->CreatePipeline(config);
|
||||
|
||||
if (m_material_resource_data->m_pipeline)
|
||||
{
|
||||
WriteUniforms(m_material_resource_data.get());
|
||||
}
|
||||
m_material_resource_data->m_processing_finished = true;
|
||||
return true;
|
||||
}
|
||||
void Retrieve() override {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<MaterialResource::Data> m_material_resource_data;
|
||||
std::shared_ptr<ShaderResource::Data> m_shader_resource_data;
|
||||
VideoCommon::GXPipelineUid* m_uid;
|
||||
FramebufferState m_frame_buffer_state;
|
||||
};
|
||||
|
||||
if (!m_processing_load_data)
|
||||
{
|
||||
auto wi = m_resource_context.shader_compiler->CreateWorkItem<WorkItem>(
|
||||
m_load_data, std::move(shader_data), &m_uid,
|
||||
g_framebuffer_manager->GetEFBFramebufferState());
|
||||
|
||||
// We don't need priority, that is already handled by the resource system
|
||||
m_resource_context.shader_compiler->QueueWorkItem(std::move(wi), 0);
|
||||
m_processing_load_data = true;
|
||||
}
|
||||
|
||||
if (!m_load_data->m_processing_finished)
|
||||
return TaskComplete::No;
|
||||
|
||||
if (!m_load_data->m_pipeline)
|
||||
{
|
||||
return TaskComplete::Error;
|
||||
}
|
||||
|
||||
std::swap(m_current_data, m_load_data);
|
||||
return TaskComplete::Yes;
|
||||
}
|
||||
|
||||
void MaterialResource::MarkAsActive()
|
||||
{
|
||||
if (!m_current_data) [[unlikely]]
|
||||
return;
|
||||
|
||||
m_resource_context.asset_cache->MarkAssetActive(m_material_asset);
|
||||
for (const auto& texture_like_resource : m_current_data->m_texture_like_resources)
|
||||
{
|
||||
if (texture_like_resource)
|
||||
texture_like_resource->MarkAsActive();
|
||||
}
|
||||
if (m_current_data->m_shader_resource)
|
||||
m_current_data->m_shader_resource->MarkAsActive();
|
||||
if (m_current_data->m_next_material)
|
||||
m_current_data->m_next_material->MarkAsActive();
|
||||
}
|
||||
|
||||
void MaterialResource::MarkAsPending()
|
||||
{
|
||||
m_resource_context.asset_cache->MarkAssetPending(m_material_asset);
|
||||
}
|
||||
|
||||
void MaterialResource::CreateTextureData(Data* data)
|
||||
{
|
||||
ShaderCode preprocessor_settings;
|
||||
|
||||
const auto& material_data = *data->m_material_data;
|
||||
data->m_texture_like_data.clear();
|
||||
data->m_texture_like_resources.clear();
|
||||
data->m_texture_like_references.clear();
|
||||
const u32 custom_sampler_index_offset = 8;
|
||||
for (u32 i = 0; i < static_cast<u32>(material_data.textures.size()); i++)
|
||||
{
|
||||
const auto& texture_and_sampler = material_data.textures[i];
|
||||
data->m_texture_like_references.push_back(TextureLikeReference{});
|
||||
|
||||
TextureAndSamplerResource* value = nullptr;
|
||||
data->m_texture_like_resources.push_back(value);
|
||||
data->m_texture_like_data.push_back(std::shared_ptr<TextureAndSamplerResource::Data>{});
|
||||
|
||||
auto& texture_like_reference = data->m_texture_like_references[i];
|
||||
|
||||
if (texture_and_sampler.asset == "")
|
||||
{
|
||||
preprocessor_settings.Write("#define HAS_SAMPLER_{} 0\n", i);
|
||||
|
||||
// For an invalid asset, force the sampler to use the default sampler
|
||||
texture_like_reference.sampler_origin =
|
||||
VideoCommon::TextureSamplerValue::SamplerOrigin::Asset;
|
||||
texture_like_reference.texture_hash = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
preprocessor_settings.Write("#define HAS_SAMPLER_{} 1\n", i);
|
||||
|
||||
texture_like_reference.sampler_origin = texture_and_sampler.sampler_origin;
|
||||
texture_like_reference.texture_hash = texture_and_sampler.texture_hash;
|
||||
}
|
||||
|
||||
texture_like_reference.sampler_index = i + custom_sampler_index_offset;
|
||||
texture_like_reference.texture = nullptr;
|
||||
}
|
||||
|
||||
data->m_preprocessor_settings = preprocessor_settings.GetBuffer();
|
||||
}
|
||||
|
||||
void MaterialResource::SetShaderKey(Data* data, GXPipelineUid* uid)
|
||||
{
|
||||
XXH3_state_t shader_key_hash;
|
||||
XXH3_INITSTATE(&shader_key_hash);
|
||||
XXH3_64bits_reset_withSeed(&shader_key_hash, static_cast<XXH64_hash_t>(1));
|
||||
|
||||
UpdateHashWithPipeline(*uid, &shader_key_hash);
|
||||
XXH3_64bits_update(&shader_key_hash, data->m_preprocessor_settings.c_str(),
|
||||
data->m_preprocessor_settings.size());
|
||||
|
||||
data->m_shader_key = XXH3_64bits_digest(&shader_key_hash);
|
||||
}
|
||||
|
||||
void MaterialResource::WriteUniforms(Data* data)
|
||||
{
|
||||
// Calculate the size in memory of the buffer
|
||||
std::size_t max_uniformdata_size = 0;
|
||||
for (const auto& property : data->m_material_data->properties)
|
||||
{
|
||||
max_uniformdata_size += VideoCommon::MaterialProperty::GetMemorySize(property);
|
||||
}
|
||||
data->m_uniform_data = Common::UniqueBuffer<u8>(max_uniformdata_size);
|
||||
|
||||
// Now write the memory
|
||||
u8* uniform_data = data->m_uniform_data.data();
|
||||
for (const auto& property : data->m_material_data->properties)
|
||||
{
|
||||
VideoCommon::MaterialProperty::WriteToMemory(uniform_data, property);
|
||||
}
|
||||
}
|
||||
} // namespace VideoCommon
|
||||
99
Source/Core/VideoCommon/Resources/MaterialResource.h
Normal file
99
Source/Core/VideoCommon/Resources/MaterialResource.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
#include "Common/SmallVector.h"
|
||||
|
||||
#include "VideoCommon/AbstractPipeline.h"
|
||||
#include "VideoCommon/Assets/MaterialAsset.h"
|
||||
#include "VideoCommon/Assets/TextureSamplerValue.h"
|
||||
#include "VideoCommon/Constants.h"
|
||||
#include "VideoCommon/GXPipelineTypes.h"
|
||||
#include "VideoCommon/RenderState.h"
|
||||
#include "VideoCommon/Resources/Resource.h"
|
||||
#include "VideoCommon/Resources/ShaderResource.h"
|
||||
#include "VideoCommon/Resources/TextureAndSamplerResource.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
class MaterialResource final : public Resource
|
||||
{
|
||||
public:
|
||||
MaterialResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid);
|
||||
|
||||
struct TextureLikeReference
|
||||
{
|
||||
SamplerState sampler;
|
||||
u32 sampler_index;
|
||||
TextureSamplerValue::SamplerOrigin sampler_origin;
|
||||
std::string_view texture_hash;
|
||||
AbstractTexture* texture;
|
||||
};
|
||||
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
AbstractPipeline* GetPipeline() const { return m_pipeline.get(); }
|
||||
std::span<const u8> GetUniforms() const { return m_uniform_data; }
|
||||
std::span<const TextureLikeReference> GetTextures() const { return m_texture_like_references; }
|
||||
MaterialResource* GetNextMaterial() const { return m_next_material; }
|
||||
|
||||
private:
|
||||
friend class MaterialResource;
|
||||
std::unique_ptr<AbstractPipeline> m_pipeline = nullptr;
|
||||
Common::UniqueBuffer<u8> m_uniform_data;
|
||||
std::shared_ptr<MaterialData> m_material_data = nullptr;
|
||||
ShaderResource* m_shader_resource = nullptr;
|
||||
|
||||
using TextureLikeResource = Resource*;
|
||||
Common::SmallVector<TextureLikeResource, VideoCommon::MAX_PIXEL_SHADER_SAMPLERS>
|
||||
m_texture_like_resources;
|
||||
|
||||
// Variant for future expansion...
|
||||
using TextureLikeData = std::variant<std::shared_ptr<TextureAndSamplerResource::Data>>;
|
||||
Common::SmallVector<TextureLikeData, VideoCommon::MAX_PIXEL_SHADER_SAMPLERS>
|
||||
m_texture_like_data;
|
||||
|
||||
Common::SmallVector<TextureLikeReference, VideoCommon::MAX_PIXEL_SHADER_SAMPLERS>
|
||||
m_texture_like_references;
|
||||
|
||||
MaterialResource* m_next_material = nullptr;
|
||||
std::size_t m_shader_key;
|
||||
std::string m_preprocessor_settings;
|
||||
std::atomic_bool m_processing_finished;
|
||||
};
|
||||
|
||||
const std::shared_ptr<Data>& GetData() const { return m_current_data; }
|
||||
void MarkAsActive() override;
|
||||
void MarkAsPending() override;
|
||||
|
||||
private:
|
||||
void ResetData() override;
|
||||
Resource::TaskComplete CollectPrimaryData() override;
|
||||
Resource::TaskComplete CollectDependencyData() override;
|
||||
Resource::TaskComplete ProcessData() override;
|
||||
|
||||
static void CreateTextureData(Data* data);
|
||||
static void SetShaderKey(Data* data, GXPipelineUid* uid);
|
||||
static void WriteUniforms(Data* data);
|
||||
|
||||
std::shared_ptr<Data> m_current_data;
|
||||
|
||||
std::shared_ptr<Data> m_load_data;
|
||||
bool m_processing_load_data = false;
|
||||
|
||||
// Note: asset cache owns the asset, we access as a reference
|
||||
MaterialAsset* m_material_asset = nullptr;
|
||||
|
||||
GXPipelineUid m_uid;
|
||||
std::unique_ptr<NativeVertexFormat> m_uid_vertex_format_copy;
|
||||
};
|
||||
} // namespace VideoCommon
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/WorkQueueThread.h"
|
||||
|
||||
#include "VideoCommon/Assets/AssetListener.h"
|
||||
#include "VideoCommon/Assets/CustomAssetLibrary.h"
|
||||
|
||||
@@ -12,8 +14,10 @@
|
||||
class AbstractTexture;
|
||||
namespace VideoCommon
|
||||
{
|
||||
class AsyncShaderCompiler;
|
||||
class CustomAssetCache;
|
||||
class CustomResourceManager;
|
||||
class TexturePool;
|
||||
|
||||
// A resource is an abstract object that maintains
|
||||
// relationships between assets (ex: a material that references a texture),
|
||||
@@ -28,6 +32,13 @@ public:
|
||||
std::shared_ptr<CustomAssetLibrary> asset_library;
|
||||
CustomAssetCache* asset_cache;
|
||||
CustomResourceManager* resource_manager;
|
||||
TexturePool* texture_pool;
|
||||
Common::AsyncWorkThreadSP* worker_queue;
|
||||
AsyncShaderCompiler* shader_compiler;
|
||||
AbstractTexture* invalid_array_texture;
|
||||
AbstractTexture* invalid_color_texture;
|
||||
AbstractTexture* invalid_cubemap_texture;
|
||||
AbstractTexture* invalid_transparent_texture;
|
||||
};
|
||||
explicit Resource(ResourceContext resource_context);
|
||||
|
||||
|
||||
311
Source/Core/VideoCommon/Resources/ShaderResource.cpp
Normal file
311
Source/Core/VideoCommon/Resources/ShaderResource.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/Resources/ShaderResource.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/Assets/CustomAssetCache.h"
|
||||
#include "VideoCommon/AsyncShaderCompiler.h"
|
||||
#include "VideoCommon/GeometryShaderGen.h"
|
||||
#include "VideoCommon/PipelineUtils.h"
|
||||
#include "VideoCommon/PixelShaderGen.h"
|
||||
#include "VideoCommon/VertexShaderGen.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::unique_ptr<AbstractShader>
|
||||
CompileGeometryShader(const GeometryShaderUid& uid, APIType api_type, ShaderHostConfig host_config)
|
||||
{
|
||||
const ShaderCode source_code =
|
||||
GenerateGeometryShaderCode(api_type, host_config, uid.GetUidData());
|
||||
return g_gfx->CreateShaderFromSource(ShaderStage::Geometry, source_code.GetBuffer(), nullptr,
|
||||
fmt::format("Geometry shader: {}", *uid.GetUidData()));
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractShader> CompilePixelShader(const PixelShaderUid& uid,
|
||||
std::string_view preprocessor_settings,
|
||||
APIType api_type,
|
||||
const ShaderHostConfig& host_config,
|
||||
RasterSurfaceShaderData* shader_data)
|
||||
{
|
||||
ShaderCode shader_code;
|
||||
|
||||
// Write any preprocessor values that were passed in
|
||||
shader_code.Write("{}", preprocessor_settings);
|
||||
|
||||
// TODO: in the future we could dynamically determine the amount of samplers
|
||||
// available, for now just hardcode to start at 8 (the first non game
|
||||
// sampler index available)
|
||||
const std::size_t custom_sampler_index_offset = 8;
|
||||
for (std::size_t i = 0; i < shader_data->samplers.size(); i++)
|
||||
{
|
||||
const auto& sampler = shader_data->samplers[i];
|
||||
std::string_view sampler_type;
|
||||
switch (sampler.type)
|
||||
{
|
||||
case AbstractTextureType::Texture_2D:
|
||||
sampler_type = "sampler2D";
|
||||
break;
|
||||
case AbstractTextureType::Texture_2DArray:
|
||||
sampler_type = "sampler2DArray";
|
||||
break;
|
||||
case AbstractTextureType::Texture_CubeMap:
|
||||
sampler_type = "samplerCube";
|
||||
break;
|
||||
};
|
||||
shader_code.Write("SAMPLER_BINDING({}) uniform {} samp_{};\n", custom_sampler_index_offset + i,
|
||||
sampler_type, sampler.name);
|
||||
|
||||
// Sampler usage is passed in from the material
|
||||
// Write a new preprocessor value with the sampler name
|
||||
// for easier code in the shader
|
||||
shader_code.Write("#if HAS_SAMPLER_{} == 1\n", i);
|
||||
shader_code.Write("#define HAS_{} 1\n", sampler.name);
|
||||
shader_code.Write("#endif\n");
|
||||
|
||||
shader_code.Write("\n");
|
||||
}
|
||||
shader_code.Write("\n");
|
||||
|
||||
// Now write the custom shader
|
||||
shader_code.Write("{}", ReplaceAll(shader_data->pixel_source, "\r\n", "\n"));
|
||||
|
||||
// Write out the uniform data
|
||||
ShaderCode uniform_code;
|
||||
for (const auto& property : shader_data->uniform_properties)
|
||||
{
|
||||
VideoCommon::ShaderProperty::WriteAsShaderCode(uniform_code, property);
|
||||
}
|
||||
if (!shader_data->uniform_properties.empty())
|
||||
uniform_code.Write("\n\n");
|
||||
|
||||
// Compile the shader
|
||||
CustomPixelContents contents{.shader = shader_code.GetBuffer(),
|
||||
.uniforms = uniform_code.GetBuffer()};
|
||||
const ShaderCode source_code =
|
||||
GeneratePixelShaderCode(api_type, host_config, uid.GetUidData(), contents);
|
||||
ShaderIncluder* shader_includer =
|
||||
shader_data->shader_includer ? &*shader_data->shader_includer : nullptr;
|
||||
return g_gfx->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer(), shader_includer,
|
||||
"Custom Pixel Shader");
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractShader> CompileVertexShader(const VertexShaderUid& uid,
|
||||
std::string_view preprocessor_settings,
|
||||
APIType api_type,
|
||||
const ShaderHostConfig& host_config,
|
||||
const RasterSurfaceShaderData& shader_data)
|
||||
{
|
||||
ShaderCode shader_code;
|
||||
|
||||
// Write any preprocessor values that were passed in
|
||||
shader_code.Write("{}", preprocessor_settings);
|
||||
|
||||
// TODO: in the future we could dynamically determine the amount of samplers
|
||||
// available, for now just hardcode to start at 8 (the first non game
|
||||
// sampler index available)
|
||||
const std::size_t custom_sampler_index_offset = 8;
|
||||
for (std::size_t i = 0; i < shader_data.samplers.size(); i++)
|
||||
{
|
||||
const auto& sampler = shader_data.samplers[i];
|
||||
std::string_view sampler_type = "";
|
||||
switch (sampler.type)
|
||||
{
|
||||
case AbstractTextureType::Texture_2D:
|
||||
sampler_type = "sampler2D";
|
||||
break;
|
||||
case AbstractTextureType::Texture_2DArray:
|
||||
sampler_type = "sampler2DArray";
|
||||
break;
|
||||
case AbstractTextureType::Texture_CubeMap:
|
||||
sampler_type = "samplerCube";
|
||||
break;
|
||||
};
|
||||
shader_code.Write("SAMPLER_BINDING({}) uniform {} samp_{};\n", custom_sampler_index_offset + i,
|
||||
sampler_type, sampler.name);
|
||||
|
||||
// Sampler usage is passed in from the material
|
||||
// Write a new preprocessor value with the sampler name
|
||||
// for easier code in the shader
|
||||
shader_code.Write("#if HAS_SAMPLER_{} == 1\n", i);
|
||||
shader_code.Write("#define HAS_{} 1\n", sampler.name);
|
||||
shader_code.Write("#endif\n");
|
||||
|
||||
shader_code.Write("\n");
|
||||
}
|
||||
shader_code.Write("\n");
|
||||
|
||||
// Now write the custom shader
|
||||
shader_code.Write("{}", ReplaceAll(shader_data.vertex_source, "\r\n", "\n"));
|
||||
|
||||
// Write out the uniform data
|
||||
ShaderCode uniform_code;
|
||||
for (const auto& property : shader_data.uniform_properties)
|
||||
{
|
||||
VideoCommon::ShaderProperty::WriteAsShaderCode(uniform_code, property);
|
||||
}
|
||||
if (!shader_data.uniform_properties.empty())
|
||||
uniform_code.Write("\n\n");
|
||||
|
||||
// Compile the shader
|
||||
CustomVertexContents contents{.shader = shader_code.GetBuffer(),
|
||||
.uniforms = uniform_code.GetBuffer()};
|
||||
const ShaderCode source_code =
|
||||
GenerateVertexShaderCode(api_type, host_config, uid.GetUidData(), contents);
|
||||
return g_gfx->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer(), nullptr,
|
||||
"Custom Vertex Shader");
|
||||
}
|
||||
} // namespace
|
||||
ShaderResource::ShaderResource(Resource::ResourceContext resource_context,
|
||||
const GXPipelineUid& pipeline_uid,
|
||||
const std::string& preprocessor_setting,
|
||||
const ShaderHostConfig& shader_host_config)
|
||||
: Resource(std::move(resource_context)), m_uid(pipeline_uid),
|
||||
m_preprocessor_settings(preprocessor_setting),
|
||||
m_shader_host_config{.bits = shader_host_config.bits}
|
||||
{
|
||||
m_shader_asset = m_resource_context.asset_cache->CreateAsset<RasterSurfaceShaderAsset>(
|
||||
m_resource_context.primary_asset_id, m_resource_context.asset_library, this);
|
||||
}
|
||||
|
||||
void ShaderResource::SetHostConfig(const ShaderHostConfig& host_config)
|
||||
{
|
||||
m_shader_host_config.bits = host_config.bits;
|
||||
}
|
||||
|
||||
void ShaderResource::MarkAsPending()
|
||||
{
|
||||
m_resource_context.asset_cache->MarkAssetPending(m_shader_asset);
|
||||
}
|
||||
|
||||
void ShaderResource::MarkAsActive()
|
||||
{
|
||||
m_resource_context.asset_cache->MarkAssetActive(m_shader_asset);
|
||||
}
|
||||
|
||||
AbstractShader* ShaderResource::Data::GetVertexShader() const
|
||||
{
|
||||
if (!m_vertex_shader)
|
||||
return nullptr;
|
||||
return m_vertex_shader.get();
|
||||
}
|
||||
|
||||
AbstractShader* ShaderResource::Data::GetPixelShader() const
|
||||
{
|
||||
if (!m_pixel_shader)
|
||||
return nullptr;
|
||||
return m_pixel_shader.get();
|
||||
}
|
||||
|
||||
AbstractShader* ShaderResource::Data::GetGeometryShader() const
|
||||
{
|
||||
if (!m_geometry_shader)
|
||||
return nullptr;
|
||||
return m_geometry_shader.get();
|
||||
}
|
||||
|
||||
bool ShaderResource::Data::IsCompiled() const
|
||||
{
|
||||
return m_vertex_shader && m_pixel_shader && (!m_needs_geometry_shader || m_geometry_shader);
|
||||
}
|
||||
|
||||
AbstractTextureType ShaderResource::Data::GetTextureType(std::size_t index)
|
||||
{
|
||||
// If the data doesn't exist, just pick one...
|
||||
if (!m_shader_data || index >= m_shader_data->samplers.size()) [[unlikely]]
|
||||
return AbstractTextureType::Texture_2D;
|
||||
|
||||
return m_shader_data->samplers[index].type;
|
||||
}
|
||||
|
||||
void ShaderResource::ResetData()
|
||||
{
|
||||
m_load_data = std::make_shared<Data>();
|
||||
m_processing_load_data = false;
|
||||
}
|
||||
|
||||
Resource::TaskComplete ShaderResource::CollectPrimaryData()
|
||||
{
|
||||
const auto shader_data = m_shader_asset->GetData();
|
||||
if (!shader_data) [[unlikely]]
|
||||
{
|
||||
return Resource::TaskComplete::No;
|
||||
}
|
||||
m_load_data->m_shader_data = shader_data;
|
||||
|
||||
return Resource::TaskComplete::Yes;
|
||||
}
|
||||
|
||||
Resource::TaskComplete ShaderResource::ProcessData()
|
||||
{
|
||||
class WorkItem final : public VideoCommon::AsyncShaderCompiler::WorkItem
|
||||
{
|
||||
public:
|
||||
WorkItem(std::shared_ptr<ShaderResource::Data> resource_data, VideoCommon::GXPipelineUid* uid,
|
||||
u32 shader_bits, std::string_view preprocessor_settings)
|
||||
: m_resource_data(std::move(resource_data)), m_uid(uid), m_shader_bits(shader_bits),
|
||||
m_preprocessor_settings(preprocessor_settings)
|
||||
{
|
||||
}
|
||||
|
||||
bool Compile() override
|
||||
{
|
||||
const ShaderHostConfig shader_host_config{.bits = m_shader_bits};
|
||||
auto actual_uid = ApplyDriverBugs(*m_uid);
|
||||
|
||||
ClearUnusedPixelShaderUidBits(g_backend_info.api_type, shader_host_config,
|
||||
&actual_uid.ps_uid);
|
||||
m_resource_data->m_needs_geometry_shader = shader_host_config.backend_geometry_shaders &&
|
||||
!actual_uid.gs_uid.GetUidData()->IsPassthrough();
|
||||
|
||||
if (m_resource_data->m_needs_geometry_shader)
|
||||
{
|
||||
m_resource_data->m_geometry_shader =
|
||||
CompileGeometryShader(actual_uid.gs_uid, g_backend_info.api_type, shader_host_config);
|
||||
}
|
||||
m_resource_data->m_pixel_shader =
|
||||
CompilePixelShader(actual_uid.ps_uid, m_preprocessor_settings, g_backend_info.api_type,
|
||||
shader_host_config, m_resource_data->m_shader_data.get());
|
||||
m_resource_data->m_vertex_shader =
|
||||
CompileVertexShader(actual_uid.vs_uid, m_preprocessor_settings, g_backend_info.api_type,
|
||||
shader_host_config, *m_resource_data->m_shader_data);
|
||||
m_resource_data->m_processing_finished = true;
|
||||
return true;
|
||||
}
|
||||
void Retrieve() override {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<ShaderResource::Data> m_resource_data;
|
||||
VideoCommon::GXPipelineUid* m_uid;
|
||||
u32 m_shader_bits;
|
||||
std::string_view m_preprocessor_settings;
|
||||
};
|
||||
|
||||
if (!m_processing_load_data)
|
||||
{
|
||||
std::string_view preprocessor_settings = m_preprocessor_settings;
|
||||
auto wi = m_resource_context.shader_compiler->CreateWorkItem<WorkItem>(
|
||||
m_load_data, &m_uid, m_shader_host_config.bits, preprocessor_settings);
|
||||
|
||||
// We don't need priority, that is already handled by the resource system
|
||||
m_resource_context.shader_compiler->QueueWorkItem(std::move(wi), 0);
|
||||
m_processing_load_data = true;
|
||||
}
|
||||
|
||||
if (!m_load_data->m_processing_finished)
|
||||
return Resource::TaskComplete::No;
|
||||
|
||||
if (!m_load_data->IsCompiled())
|
||||
return Resource::TaskComplete::Error;
|
||||
|
||||
std::swap(m_current_data, m_load_data);
|
||||
return Resource::TaskComplete::Yes;
|
||||
}
|
||||
} // namespace VideoCommon
|
||||
67
Source/Core/VideoCommon/Resources/ShaderResource.h
Normal file
67
Source/Core/VideoCommon/Resources/ShaderResource.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "VideoCommon/Resources/Resource.h"
|
||||
|
||||
#include "VideoCommon/Assets/ShaderAsset.h"
|
||||
#include "VideoCommon/GXPipelineTypes.h"
|
||||
#include "VideoCommon/ShaderGenCommon.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
class ShaderResource final : public Resource
|
||||
{
|
||||
public:
|
||||
ShaderResource(Resource::ResourceContext resource_context, const GXPipelineUid& pipeline_uid,
|
||||
const std::string& preprocessor_settings,
|
||||
const ShaderHostConfig& shader_host_config);
|
||||
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
AbstractShader* GetVertexShader() const;
|
||||
AbstractShader* GetPixelShader() const;
|
||||
AbstractShader* GetGeometryShader() const;
|
||||
|
||||
bool IsCompiled() const;
|
||||
AbstractTextureType GetTextureType(std::size_t index);
|
||||
|
||||
private:
|
||||
friend class ShaderResource;
|
||||
std::unique_ptr<AbstractShader> m_vertex_shader;
|
||||
std::unique_ptr<AbstractShader> m_pixel_shader;
|
||||
std::unique_ptr<AbstractShader> m_geometry_shader;
|
||||
std::shared_ptr<RasterSurfaceShaderData> m_shader_data;
|
||||
bool m_needs_geometry_shader = false;
|
||||
std::atomic_bool m_processing_finished;
|
||||
};
|
||||
|
||||
// Changes the shader host config. Shaders should be reloaded afterwards.
|
||||
void SetHostConfig(const ShaderHostConfig& host_config);
|
||||
const std::shared_ptr<Data>& GetData() const { return m_current_data; }
|
||||
|
||||
void MarkAsActive() override;
|
||||
void MarkAsPending() override;
|
||||
|
||||
private:
|
||||
void ResetData() override;
|
||||
Resource::TaskComplete CollectPrimaryData() override;
|
||||
TaskComplete ProcessData() override;
|
||||
|
||||
// Note: asset cache owns the asset, we access as a reference
|
||||
RasterSurfaceShaderAsset* m_shader_asset = nullptr;
|
||||
|
||||
std::shared_ptr<Data> m_current_data;
|
||||
std::shared_ptr<Data> m_load_data;
|
||||
|
||||
bool m_processing_load_data = false;
|
||||
|
||||
ShaderHostConfig m_shader_host_config;
|
||||
GXPipelineUid m_uid;
|
||||
std::string m_preprocessor_settings;
|
||||
};
|
||||
} // namespace VideoCommon
|
||||
103
Source/Core/VideoCommon/Resources/TextureAndSamplerResource.cpp
Normal file
103
Source/Core/VideoCommon/Resources/TextureAndSamplerResource.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/Resources/TextureAndSamplerResource.h"
|
||||
|
||||
#include "VideoCommon/Assets/CustomAssetCache.h"
|
||||
#include "VideoCommon/Resources/TexturePool.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
TextureAndSamplerResource::TextureAndSamplerResource(Resource::ResourceContext resource_context)
|
||||
: Resource(std::move(resource_context))
|
||||
{
|
||||
m_texture_and_sampler_asset = m_resource_context.asset_cache->CreateAsset<TextureAndSamplerAsset>(
|
||||
m_resource_context.primary_asset_id, m_resource_context.asset_library, this);
|
||||
}
|
||||
|
||||
void TextureAndSamplerResource::MarkAsActive()
|
||||
{
|
||||
m_resource_context.asset_cache->MarkAssetActive(m_texture_and_sampler_asset);
|
||||
}
|
||||
|
||||
void TextureAndSamplerResource::MarkAsPending()
|
||||
{
|
||||
m_resource_context.asset_cache->MarkAssetPending(m_texture_and_sampler_asset);
|
||||
}
|
||||
|
||||
const std::shared_ptr<TextureAndSamplerResource::Data>& TextureAndSamplerResource::GetData() const
|
||||
{
|
||||
return m_current_data;
|
||||
}
|
||||
|
||||
void TextureAndSamplerResource::ResetData()
|
||||
{
|
||||
m_load_data = std::make_shared<Data>();
|
||||
}
|
||||
|
||||
Resource::TaskComplete TextureAndSamplerResource::CollectPrimaryData()
|
||||
{
|
||||
m_load_data->m_texture_and_sampler_data = m_texture_and_sampler_asset->GetData();
|
||||
if (!m_load_data->m_texture_and_sampler_data)
|
||||
return Resource::TaskComplete::No;
|
||||
|
||||
auto& texture_data = m_load_data->m_texture_and_sampler_data->texture_data;
|
||||
if (texture_data.m_slices.empty())
|
||||
return Resource::TaskComplete::Error;
|
||||
|
||||
if (texture_data.m_slices[0].m_levels.empty())
|
||||
return Resource::TaskComplete::Error;
|
||||
|
||||
const auto& first_level = texture_data.m_slices[0].m_levels[0];
|
||||
|
||||
auto& config = m_load_data->m_config;
|
||||
config.format = first_level.format;
|
||||
config.flags = 0;
|
||||
config.layers = 1;
|
||||
config.levels = 1;
|
||||
config.type = m_load_data->m_texture_and_sampler_data->type;
|
||||
config.samples = 1;
|
||||
|
||||
config.width = first_level.width;
|
||||
config.height = first_level.height;
|
||||
|
||||
return Resource::TaskComplete::Yes;
|
||||
}
|
||||
|
||||
Resource::TaskComplete TextureAndSamplerResource::ProcessData()
|
||||
{
|
||||
auto texture = m_resource_context.texture_pool->AllocateTexture(m_load_data->m_config);
|
||||
if (!texture) [[unlikely]]
|
||||
return Resource::TaskComplete::Error;
|
||||
|
||||
m_load_data->m_texture = std::move(texture);
|
||||
|
||||
auto& texture_data = m_load_data->m_texture_and_sampler_data->texture_data;
|
||||
for (std::size_t slice_index = 0; slice_index < texture_data.m_slices.size(); slice_index++)
|
||||
{
|
||||
auto& slice = texture_data.m_slices[slice_index];
|
||||
for (u32 level_index = 0; level_index < static_cast<u32>(slice.m_levels.size()); ++level_index)
|
||||
{
|
||||
auto& level = slice.m_levels[level_index];
|
||||
m_load_data->m_texture->Load(level_index, level.width, level.height, level.row_length,
|
||||
level.data.data(), level.data.size(),
|
||||
static_cast<u32>(slice_index));
|
||||
}
|
||||
}
|
||||
std::swap(m_current_data, m_load_data);
|
||||
|
||||
// Release old data back to the pool
|
||||
if (m_load_data)
|
||||
m_resource_context.texture_pool->ReleaseTexture(std::move(m_load_data->m_texture));
|
||||
|
||||
return Resource::TaskComplete::Yes;
|
||||
}
|
||||
|
||||
void TextureAndSamplerResource::OnUnloadRequested()
|
||||
{
|
||||
if (!m_current_data)
|
||||
return;
|
||||
m_resource_context.texture_pool->ReleaseTexture(std::move(m_current_data->m_texture));
|
||||
m_current_data = nullptr;
|
||||
}
|
||||
} // namespace VideoCommon
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2025 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VideoCommon/Resources/Resource.h"
|
||||
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
#include "VideoCommon/Assets/TextureAsset.h"
|
||||
|
||||
namespace VideoCommon
|
||||
{
|
||||
class TextureAndSamplerResource final : public Resource
|
||||
{
|
||||
public:
|
||||
explicit TextureAndSamplerResource(Resource::ResourceContext resource_context);
|
||||
void MarkAsActive() override;
|
||||
void MarkAsPending() override;
|
||||
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
AbstractTexture* GetTexture() const { return m_texture.get(); }
|
||||
const SamplerState& GetSampler() const { return m_texture_and_sampler_data->sampler; }
|
||||
|
||||
private:
|
||||
friend class TextureAndSamplerResource;
|
||||
|
||||
std::shared_ptr<TextureAndSamplerData> m_texture_and_sampler_data;
|
||||
std::unique_ptr<AbstractTexture> m_texture;
|
||||
TextureConfig m_config;
|
||||
};
|
||||
|
||||
const std::shared_ptr<Data>& GetData() const;
|
||||
|
||||
private:
|
||||
void ResetData() override;
|
||||
TaskComplete CollectPrimaryData() override;
|
||||
TaskComplete ProcessData() override;
|
||||
|
||||
void OnUnloadRequested() override;
|
||||
|
||||
// Note: asset cache owns the asset, we access as a reference
|
||||
TextureAndSamplerAsset* m_texture_and_sampler_asset = nullptr;
|
||||
|
||||
std::shared_ptr<Data> m_current_data;
|
||||
std::shared_ptr<Data> m_load_data;
|
||||
};
|
||||
} // namespace VideoCommon
|
||||
Reference in New Issue
Block a user