GPU: Implement texture dumping and replacement
This commit is contained in:
parent
b80e146ec7
commit
9d3ed39432
|
@ -95,6 +95,8 @@ add_library(core
|
|||
spu.h
|
||||
system.cpp
|
||||
system.h
|
||||
texture_dumper.cpp
|
||||
texture_dumper.h
|
||||
texture_replacements.cpp
|
||||
texture_replacements.h
|
||||
timers.cpp
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
<ClCompile Include="spu.cpp" />
|
||||
<ClCompile Include="system.cpp" />
|
||||
<ClCompile Include="texture_replacements.cpp" />
|
||||
<ClCompile Include="texture_dumper.cpp" />
|
||||
<ClCompile Include="timers.cpp" />
|
||||
<ClCompile Include="timing_event.cpp" />
|
||||
</ItemGroup>
|
||||
|
@ -150,6 +151,7 @@
|
|||
<ClInclude Include="spu.h" />
|
||||
<ClInclude Include="system.h" />
|
||||
<ClInclude Include="texture_replacements.h" />
|
||||
<ClInclude Include="texture_dumper.h" />
|
||||
<ClInclude Include="timers.h" />
|
||||
<ClInclude Include="timing_event.h" />
|
||||
<ClInclude Include="types.h" />
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
<ClCompile Include="gpu_hw_d3d12.cpp" />
|
||||
<ClCompile Include="host.cpp" />
|
||||
<ClCompile Include="game_database.cpp" />
|
||||
<ClCompile Include="texture_dumper.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="types.h" />
|
||||
|
@ -124,5 +125,6 @@
|
|||
<ClInclude Include="host_settings.h" />
|
||||
<ClInclude Include="achievements.h" />
|
||||
<ClInclude Include="game_database.h" />
|
||||
<ClInclude Include="texture_dumper.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "interrupt_controller.h"
|
||||
#include "system.h"
|
||||
#include "texture_replacements.h"
|
||||
#include "texture_dumper.h"
|
||||
Log_SetChannel(GPU);
|
||||
|
||||
#define CHECK_COMMAND_SIZE(num_words) \
|
||||
|
@ -462,7 +463,12 @@ bool GPU::HandleFillRectangleCommand()
|
|||
Log_DebugPrintf("Fill VRAM rectangle offset=(%u,%u), size=(%u,%u)", dst_x, dst_y, width, height);
|
||||
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
if (g_settings.texture_replacements.dump_textures)
|
||||
TextureDumper::AddClear(dst_x, dst_y, width, height);
|
||||
|
||||
FillVRAM(dst_x, dst_y, width, height, color);
|
||||
}
|
||||
|
||||
m_stats.num_vram_fills++;
|
||||
AddCommandTicks(46 + ((width / 8) + 9) * height);
|
||||
|
@ -513,10 +519,10 @@ void GPU::FinishVRAMWrite()
|
|||
m_blit_buffer.data(), true);
|
||||
}
|
||||
|
||||
if (g_settings.texture_replacements.ShouldDumpVRAMWrite(m_vram_transfer.width, m_vram_transfer.height))
|
||||
if (g_settings.texture_replacements.dump_textures)
|
||||
{
|
||||
g_texture_replacements.DumpVRAMWrite(m_vram_transfer.width, m_vram_transfer.height,
|
||||
reinterpret_cast<const u16*>(m_blit_buffer.data()));
|
||||
TextureDumper::AddVRAMWrite(m_vram_transfer.x, m_vram_transfer.y, m_vram_transfer.width, m_vram_transfer.height,
|
||||
reinterpret_cast<const u16*>(m_blit_buffer.data()));
|
||||
}
|
||||
|
||||
UpdateVRAM(m_vram_transfer.x, m_vram_transfer.y, m_vram_transfer.width, m_vram_transfer.height,
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "pgxp.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "texture_dumper.h"
|
||||
#include "texture_replacements.h"
|
||||
#include "util/state_wrapper.h"
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
@ -63,6 +65,7 @@ bool GPU_HW::Initialize()
|
|||
m_texture_filtering = g_settings.gpu_texture_filter;
|
||||
m_using_uv_limits = ShouldUseUVLimits();
|
||||
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
|
||||
m_texture_replacements = g_settings.texture_replacements.enable_texture_replacements;
|
||||
m_downsample_mode = GetDownsampleMode(m_resolution_scale);
|
||||
|
||||
if (m_multisamples != g_settings.gpu_multisamples)
|
||||
|
@ -90,6 +93,12 @@ bool GPU_HW::Initialize()
|
|||
"OSDMessage", "Adaptive downsampling is not supported with the current renderer, using box filter instead."),
|
||||
20.0f);
|
||||
}
|
||||
if (m_texture_replacements && !SetupTextureReplacementTexture())
|
||||
{
|
||||
Host::AddOSDMessage(
|
||||
Host::TranslateStdString("OSDMessage", "Failed to setup texture replacements, original textures will be used."),
|
||||
20.0f);
|
||||
}
|
||||
|
||||
m_pgxp_depth_buffer = g_settings.UsingPGXPDepthBuffer();
|
||||
|
||||
|
@ -148,7 +157,8 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
|
|||
m_true_color != g_settings.gpu_true_color || m_per_sample_shading != per_sample_shading ||
|
||||
m_scaled_dithering != g_settings.gpu_scaled_dithering || m_texture_filtering != g_settings.gpu_texture_filter ||
|
||||
m_using_uv_limits != use_uv_limits || m_chroma_smoothing != g_settings.gpu_24bit_chroma_smoothing ||
|
||||
m_downsample_mode != downsample_mode || m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer());
|
||||
m_downsample_mode != downsample_mode || m_pgxp_depth_buffer != g_settings.UsingPGXPDepthBuffer()) ||
|
||||
m_texture_replacements != g_settings.texture_replacements.enable_texture_replacements;
|
||||
|
||||
if (m_resolution_scale != resolution_scale)
|
||||
{
|
||||
|
@ -183,6 +193,7 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
|
|||
m_texture_filtering = g_settings.gpu_texture_filter;
|
||||
m_using_uv_limits = use_uv_limits;
|
||||
m_chroma_smoothing = g_settings.gpu_24bit_chroma_smoothing;
|
||||
m_texture_replacements = g_settings.texture_replacements.enable_texture_replacements;
|
||||
m_downsample_mode = downsample_mode;
|
||||
|
||||
if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
|
||||
|
@ -196,6 +207,16 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
|
|||
ClearDepthBuffer();
|
||||
}
|
||||
|
||||
if (!SetupTextureReplacementTexture() && m_texture_replacements)
|
||||
{
|
||||
Host::AddOSDMessage(
|
||||
Host::TranslateStdString("OSDMessage", "Failed to setup texture replacements, original textures will be used."),
|
||||
20.0f);
|
||||
|
||||
m_texture_replacements = false;
|
||||
*shaders_changed = true;
|
||||
}
|
||||
|
||||
UpdateSoftwareRenderer(true);
|
||||
|
||||
PrintSettingsToLog();
|
||||
|
@ -627,8 +648,20 @@ void GPU_HW::LoadVertices()
|
|||
if (rc.quad_polygon && m_resolution_scale > 1)
|
||||
HandleFlippedQuadTextureCoordinates(vertices.data());
|
||||
|
||||
if (m_using_uv_limits && textured)
|
||||
ComputePolygonUVLimits(vertices.data(), num_vertices);
|
||||
if (textured)
|
||||
{
|
||||
if (m_using_uv_limits)
|
||||
ComputePolygonUVLimits(vertices.data(), num_vertices);
|
||||
if (g_settings.texture_replacements.dump_textures)
|
||||
{
|
||||
if (!m_using_uv_limits)
|
||||
ComputePolygonUVLimits(vertices.data(), num_vertices);
|
||||
|
||||
TextureDumper::AddDraw(m_draw_mode.mode_reg.bits, m_draw_mode.palette_reg, vertices[0].uv_limits & 0xFF,
|
||||
(vertices[0].uv_limits >> 8) & 0xFF, (vertices[0].uv_limits >> 16) & 0xFF,
|
||||
(vertices[0].uv_limits >> 24), rc.transparency_enable);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsDrawingAreaIsValid())
|
||||
return;
|
||||
|
@ -765,6 +798,35 @@ void GPU_HW::LoadVertices()
|
|||
if (!IsDrawingAreaIsValid())
|
||||
return;
|
||||
|
||||
if (rc.texture_enable && g_settings.texture_replacements.dump_textures)
|
||||
{
|
||||
#if 0
|
||||
const s32 offset_x =
|
||||
(pos_x < static_cast<s32>(m_drawing_area.left)) ? (static_cast<s32>(m_drawing_area.left) - pos_x) : 0;
|
||||
const s32 offset_y =
|
||||
(pos_y < static_cast<s32>(m_drawing_area.top)) ? (static_cast<s32>(m_drawing_area.top) - pos_y) : 0;
|
||||
const s32 end_x = pos_x + rectangle_width;
|
||||
const s32 end_y = pos_y + rectangle_height;
|
||||
const s32 reduce_x =
|
||||
(end_x > static_cast<s32>(m_drawing_area.right)) ? (end_x - static_cast<s32>(m_drawing_area.right)) : 0;
|
||||
const s32 reduce_y =
|
||||
(end_y > static_cast<s32>(m_drawing_area.bottom)) ? (end_y - static_cast<s32>(m_drawing_area.bottom)) : 0;
|
||||
|
||||
if ((offset_x + reduce_x) <= rectangle_width && (offset_y + reduce_y) <= rectangle_height)
|
||||
{
|
||||
TextureDumper::AddDraw(m_draw_mode.mode_reg.bits, m_draw_mode.palette_reg,
|
||||
static_cast<s32>(orig_tex_left) + offset_x, static_cast<s32>(orig_tex_top) + offset_y,
|
||||
static_cast<s32>(orig_tex_left) + offset_x + (rectangle_width - offset_x - reduce_x),
|
||||
static_cast<s32>(orig_tex_top) + offset_y + (rectangle_height - offset_y - reduce_y),
|
||||
rc.transparency_enable);
|
||||
}
|
||||
#else
|
||||
TextureDumper::AddDraw(m_draw_mode.mode_reg.bits, m_draw_mode.palette_reg, orig_tex_left, orig_tex_top,
|
||||
orig_tex_left + rectangle_width, orig_tex_top + rectangle_height,
|
||||
rc.transparency_enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
// we can split the rectangle up into potentially 8 quads
|
||||
SetBatchDepthBuffer(false);
|
||||
DebugAssert(GetBatchVertexSpace() >= MAX_VERTICES_FOR_RECTANGLE);
|
||||
|
|
|
@ -33,6 +33,8 @@ public:
|
|||
GPU_HW();
|
||||
virtual ~GPU_HW();
|
||||
|
||||
ALWAYS_INLINE bool IsTextureReplacementEnabled() const { return m_texture_replacements; }
|
||||
|
||||
const Threading::Thread* GetSWThread() const override;
|
||||
|
||||
virtual bool Initialize() override;
|
||||
|
@ -43,6 +45,10 @@ public:
|
|||
std::tuple<u32, u32> GetEffectiveDisplayResolution(bool scaled = true) override final;
|
||||
std::tuple<u32, u32> GetFullDisplayResolution(bool scaled = true) override final;
|
||||
|
||||
virtual void UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride) = 0;
|
||||
virtual void InvalidateTextureReplacements() = 0;
|
||||
|
||||
protected:
|
||||
enum : u32
|
||||
{
|
||||
|
@ -205,6 +211,7 @@ protected:
|
|||
virtual void UnmapBatchVertexPointer(u32 used_vertices) = 0;
|
||||
virtual void UploadUniformBuffer(const void* uniforms, u32 uniforms_size) = 0;
|
||||
virtual void DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices) = 0;
|
||||
virtual bool SetupTextureReplacementTexture() = 0;
|
||||
|
||||
u32 CalculateResolutionScale() const;
|
||||
GPUDownsampleMode GetDownsampleMode(u32 resolution_scale) const;
|
||||
|
@ -215,6 +222,11 @@ protected:
|
|||
return (m_downsample_mode != GPUDownsampleMode::Disabled && !m_GPUSTAT.display_area_color_depth_24);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool IsFullVRAMUpload(u32 x, u32 y, u32 width, u32 height)
|
||||
{
|
||||
return (x == 0 && y == 0 && width == VRAM_WIDTH && height == VRAM_HEIGHT);
|
||||
}
|
||||
|
||||
void SetFullVRAMDirtyRectangle()
|
||||
{
|
||||
m_vram_dirty_rect.Set(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
|
||||
|
@ -377,6 +389,7 @@ protected:
|
|||
BitField<u8, bool, 3, 1> m_per_sample_shading;
|
||||
BitField<u8, bool, 4, 1> m_scaled_dithering;
|
||||
BitField<u8, bool, 5, 1> m_chroma_smoothing;
|
||||
BitField<u8, bool, 6, 1> m_texture_replacements;
|
||||
|
||||
u8 bits = 0;
|
||||
};
|
||||
|
|
|
@ -159,8 +159,20 @@ void GPU_HW_D3D11::RestoreGraphicsAPIState()
|
|||
m_context->IASetInputLayout(m_batch_input_layout.Get());
|
||||
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
m_context->GSSetShader(nullptr, nullptr, 0);
|
||||
m_context->PSSetShaderResources(0, 1, m_vram_read_texture.GetD3DSRVArray());
|
||||
m_context->PSSetSamplers(0, 1, m_point_sampler_state.GetAddressOf());
|
||||
|
||||
if (!g_settings.texture_replacements.enable_texture_replacements)
|
||||
{
|
||||
m_context->PSSetShaderResources(0, 1, m_vram_read_texture.GetD3DSRVArray());
|
||||
m_context->PSSetSamplers(0, 1, m_point_sampler_state.GetAddressOf());
|
||||
}
|
||||
else
|
||||
{
|
||||
ID3D11ShaderResourceView* srvs[2] = {m_vram_read_texture.GetD3DSRV(), m_texture_replacement_texture.GetD3DSRV()};
|
||||
ID3D11SamplerState* samplers[2] = {m_point_sampler_state.Get(), m_point_sampler_state.Get()};
|
||||
m_context->PSSetShaderResources(0, countof(srvs), srvs);
|
||||
m_context->PSSetSamplers(0, countof(samplers), samplers);
|
||||
}
|
||||
|
||||
m_context->OMSetRenderTargets(1, m_vram_texture.GetD3DRTVArray(), m_vram_depth_view.Get());
|
||||
m_context->RSSetState(m_cull_none_rasterizer_state.Get());
|
||||
SetViewport(0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
|
||||
|
@ -175,6 +187,9 @@ void GPU_HW_D3D11::UpdateSettings()
|
|||
bool framebuffer_changed, shaders_changed;
|
||||
UpdateHWSettings(&framebuffer_changed, &shaders_changed);
|
||||
|
||||
if (!SetupTextureReplacementTexture())
|
||||
shaders_changed = true;
|
||||
|
||||
if (framebuffer_changed)
|
||||
{
|
||||
RestoreGraphicsAPIState();
|
||||
|
@ -202,6 +217,71 @@ void GPU_HW_D3D11::UpdateSettings()
|
|||
}
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::SetupTextureReplacementTexture()
|
||||
{
|
||||
const bool is_enabled = static_cast<bool>(m_texture_replacement_texture);
|
||||
if (is_enabled == m_texture_replacements)
|
||||
{
|
||||
if (!is_enabled ||
|
||||
(m_texture_replacement_texture.GetWidth() == g_texture_replacements.GetScaledReplacementTextureWidth() &&
|
||||
m_texture_replacement_texture.GetHeight() == g_texture_replacements.GetScaledReplacementTextureHeight()))
|
||||
{
|
||||
// no changes
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_texture_replacement_texture.Destroy();
|
||||
|
||||
if (m_texture_replacements)
|
||||
{
|
||||
const u32 width = g_texture_replacements.GetScaledReplacementTextureWidth();
|
||||
const u32 height = g_texture_replacements.GetScaledReplacementTextureHeight();
|
||||
if (!m_texture_replacement_texture.Create(m_device.Get(), width, height,
|
||||
TextureReplacements::TEXTURE_REPLACEMENT_PAGE_COUNT, 1, 1,
|
||||
REPLACEMENT_TEXTURE_FORMAT, D3D11_BIND_SHADER_RESOURCE))
|
||||
{
|
||||
m_texture_replacement_texture.Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
g_texture_replacements.ReuploadReplacementTextures();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride)
|
||||
{
|
||||
if (!m_texture_replacement_texture)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
if (!m_texture_replacement_staging_texture.Map(m_context.Get(), true))
|
||||
return;
|
||||
|
||||
m_texture_replacement_staging_texture.WritePixels(0, 0, data_width, data_height, data_stride,
|
||||
reinterpret_cast<const u32*>(data));
|
||||
m_texture_replacement_staging_texture.Unmap(m_context.Get());
|
||||
|
||||
const CD3D11_BOX src_box(0, 0, 0, data_width, data_height, 1);
|
||||
m_context->CopySubresourceRegion(m_texture_replacement_texture, D3D11CalcSubresource(0, page_index, 1), page_x,
|
||||
page_y, 0, m_texture_replacement_staging_texture.GetD3DTexture(), 0, &src_box);
|
||||
#else
|
||||
const CD3D11_BOX dst_box(page_x, page_y, 0, page_x + data_width, page_y + data_height, 1);
|
||||
m_context->UpdateSubresource(m_texture_replacement_texture, D3D11CalcSubresource(0, page_index, 1), &dst_box, data,
|
||||
data_stride, data_stride * data_height);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::InvalidateTextureReplacements()
|
||||
{
|
||||
m_texture_replacement_texture.Destroy();
|
||||
if (!SetupTextureReplacementTexture())
|
||||
Panic("Failed to reallocate texture replacement textures");
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::MapBatchVertexPointer(u32 required_vertices)
|
||||
{
|
||||
DebugAssert(!m_batch_start_vertex_ptr);
|
||||
|
@ -567,7 +647,7 @@ bool GPU_HW_D3D11::CompileShaders()
|
|||
{
|
||||
const std::string ps = shadergen.GenerateBatchFragmentShader(
|
||||
static_cast<BatchRenderMode>(render_mode), static_cast<GPUTextureMode>(texture_mode),
|
||||
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing));
|
||||
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing), m_texture_replacements);
|
||||
|
||||
m_batch_pixel_shaders[render_mode][texture_mode][dithering][interlacing] =
|
||||
shader_cache.GetPixelShader(m_device.Get(), ps);
|
||||
|
@ -991,14 +1071,20 @@ void GPU_HW_D3D11::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* d
|
|||
const Common::Rectangle<u32> bounds = GetVRAMTransferBounds(x, y, width, height);
|
||||
GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask);
|
||||
|
||||
if (!check_mask)
|
||||
if (!check_mask && !IsFullVRAMUpload(x, y, width, height))
|
||||
{
|
||||
const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data);
|
||||
if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale,
|
||||
width * m_resolution_scale, height * m_resolution_scale))
|
||||
if (g_settings.texture_replacements.enable_vram_write_replacements)
|
||||
{
|
||||
return;
|
||||
const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data);
|
||||
if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale,
|
||||
width * m_resolution_scale, height * m_resolution_scale))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_texture_replacements)
|
||||
g_texture_replacements.UploadReplacementTextures(x, y, width, height, data);
|
||||
}
|
||||
|
||||
const u32 num_pixels = width * height;
|
||||
|
|
|
@ -29,6 +29,10 @@ public:
|
|||
void RestoreGraphicsAPIState() override;
|
||||
void UpdateSettings() override;
|
||||
|
||||
void UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride) override;
|
||||
void InvalidateTextureReplacements() override;
|
||||
|
||||
protected:
|
||||
void ClearDisplay() override;
|
||||
void UpdateDisplay() override;
|
||||
|
@ -44,6 +48,7 @@ protected:
|
|||
void UnmapBatchVertexPointer(u32 used_vertices) override;
|
||||
void UploadUniformBuffer(const void* data, u32 data_size) override;
|
||||
void DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices) override;
|
||||
bool SetupTextureReplacementTexture() override;
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
|
@ -52,6 +57,8 @@ private:
|
|||
MAX_UNIFORM_BUFFER_SIZE = 64
|
||||
};
|
||||
|
||||
static constexpr DXGI_FORMAT REPLACEMENT_TEXTURE_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
void SetCapabilities();
|
||||
bool CreateFramebuffer();
|
||||
void ClearFramebuffer();
|
||||
|
@ -128,6 +135,7 @@ private:
|
|||
std::array<std::array<ComPtr<ID3D11PixelShader>, 3>, 2> m_display_pixel_shaders; // [depth_24][interlaced]
|
||||
|
||||
D3D11::Texture m_vram_replacement_texture;
|
||||
D3D11::Texture m_texture_replacement_texture;
|
||||
|
||||
// downsampling
|
||||
ComPtr<ID3D11PixelShader> m_downsample_first_pass_pixel_shader;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "gpu_hw_shadergen.h"
|
||||
#include "host_display.h"
|
||||
#include "system.h"
|
||||
#include "texture_replacements.h"
|
||||
Log_SetChannel(GPU_HW_D3D12);
|
||||
|
||||
GPU_HW_D3D12::GPU_HW_D3D12() = default;
|
||||
|
@ -117,11 +118,21 @@ void GPU_HW_D3D12::RestoreGraphicsAPIState()
|
|||
cmdlist->IASetVertexBuffers(0, 1, &vbv);
|
||||
cmdlist->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
|
||||
cmdlist->SetGraphicsRootSignature(m_batch_root_signature.Get());
|
||||
if (m_texture_replacements)
|
||||
{
|
||||
cmdlist->SetGraphicsRootSignature(m_texture_replacement_root_signature.Get());
|
||||
cmdlist->SetGraphicsRootDescriptorTable(1, m_texture_replacement_srv_handle.gpu_handle);
|
||||
cmdlist->SetGraphicsRootDescriptorTable(2, m_texture_replacement_sampler_handle.gpu_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmdlist->SetGraphicsRootSignature(m_batch_root_signature.Get());
|
||||
cmdlist->SetGraphicsRootDescriptorTable(1, m_vram_read_texture.GetSRVDescriptor().gpu_handle);
|
||||
cmdlist->SetGraphicsRootDescriptorTable(2, m_point_sampler.gpu_handle);
|
||||
}
|
||||
|
||||
cmdlist->SetGraphicsRootConstantBufferView(0,
|
||||
m_uniform_stream_buffer.GetGPUPointer() + m_current_uniform_buffer_offset);
|
||||
cmdlist->SetGraphicsRootDescriptorTable(1, m_vram_read_texture.GetSRVDescriptor().gpu_handle);
|
||||
cmdlist->SetGraphicsRootDescriptorTable(2, m_point_sampler.gpu_handle);
|
||||
|
||||
D3D12::SetViewport(cmdlist, 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
|
||||
|
||||
|
@ -167,6 +178,154 @@ void GPU_HW_D3D12::UpdateSettings()
|
|||
}
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D12::SetupTextureReplacementTexture()
|
||||
{
|
||||
const bool is_enabled = static_cast<bool>(m_texture_replacement_texture);
|
||||
if (is_enabled == m_texture_replacements)
|
||||
{
|
||||
if (!is_enabled ||
|
||||
(m_texture_replacement_texture.GetWidth() == g_texture_replacements.GetScaledReplacementTextureWidth() &&
|
||||
m_texture_replacement_texture.GetHeight() == g_texture_replacements.GetScaledReplacementTextureHeight()))
|
||||
{
|
||||
// no changes
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
g_d3d12_context->ExecuteCommandList(true);
|
||||
m_texture_replacement_texture.Destroy(false);
|
||||
|
||||
if (m_texture_replacements)
|
||||
{
|
||||
#if 1
|
||||
Panic("Fixme");
|
||||
#else
|
||||
const u32 width = g_texture_replacements.GetScaledReplacementTextureWidth();
|
||||
const u32 height = g_texture_replacements.GetScaledReplacementTextureHeight();
|
||||
if (!m_texture_replacement_texture.Create(width, height, TextureReplacements::TEXTURE_REPLACEMENT_PAGE_COUNT, 1, 1,
|
||||
REPLACEMENT_TEXTURE_FORMAT, REPLACEMENT_TEXTURE_FORMAT,
|
||||
REPLACEMENT_TEXTURE_FORMAT, DXGI_FORMAT_UNKNOWN,
|
||||
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12::SetObjectName(m_texture_replacement_texture, "Texture replacement texture");
|
||||
InvalidateTextureReplacements();
|
||||
|
||||
if (!m_texture_replacement_srv_handle)
|
||||
{
|
||||
g_d3d12_context->GetDescriptorHeapManager().Allocate(&m_texture_replacement_srv_handle,
|
||||
TEXTURE_REPLACEMENT_BATCH_TEXTURE_COUNT);
|
||||
}
|
||||
if (!m_texture_replacement_sampler_handle)
|
||||
{
|
||||
g_d3d12_context->GetSamplerHeapManager().Allocate(&m_texture_replacement_sampler_handle,
|
||||
TEXTURE_REPLACEMENT_BATCH_TEXTURE_COUNT);
|
||||
}
|
||||
|
||||
UpdateTextureReplacementSRV();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_texture_replacement_srv_handle)
|
||||
{
|
||||
g_d3d12_context->GetDescriptorHeapManager().Free(&m_texture_replacement_srv_handle,
|
||||
TEXTURE_REPLACEMENT_BATCH_TEXTURE_COUNT);
|
||||
}
|
||||
if (m_texture_replacement_sampler_handle)
|
||||
{
|
||||
g_d3d12_context->GetSamplerHeapManager().Free(&m_texture_replacement_sampler_handle,
|
||||
TEXTURE_REPLACEMENT_BATCH_TEXTURE_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_texture_replacements)
|
||||
g_texture_replacements.ReuploadReplacementTextures();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D12::UpdateTextureReplacementSRV()
|
||||
{
|
||||
#if 0
|
||||
if (!m_texture_replacement_texture || !m_vram_read_texture)
|
||||
return;
|
||||
|
||||
ID3D12Device* dev = g_d3d12_context->GetDevice();
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC vram_desc = {m_vram_read_texture.GetFormat(), D3D12_SRV_DIMENSION_TEXTURE2D,
|
||||
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
|
||||
vram_desc.Texture2D.MipLevels = m_vram_read_texture.GetLevels();
|
||||
dev->CreateShaderResourceView(m_vram_read_texture, &vram_desc, m_texture_replacement_srv_handle);
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC replacement_desc = {m_texture_replacement_texture.GetFormat(),
|
||||
D3D12_SRV_DIMENSION_TEXTURE2DARRAY,
|
||||
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
|
||||
replacement_desc.Texture2DArray.MipLevels = m_texture_replacement_texture.GetLevels();
|
||||
replacement_desc.Texture2DArray.ArraySize = m_texture_replacement_texture.GetLayers();
|
||||
dev->CreateShaderResourceView(
|
||||
m_texture_replacement_texture, &replacement_desc,
|
||||
g_d3d12_context->GetDescriptorHeapManager().OffsetCPUHandle(m_texture_replacement_srv_handle, 1));
|
||||
|
||||
D3D12_SAMPLER_DESC sd;
|
||||
D3D12::SetDefaultSampler(&sd);
|
||||
sd.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
sd.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
sd.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
||||
dev->CreateSampler(&sd, m_texture_replacement_sampler_handle);
|
||||
|
||||
sd.Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
|
||||
dev->CreateSampler(
|
||||
&sd, g_d3d12_context->GetDescriptorHeapManager().OffsetCPUHandle(m_texture_replacement_sampler_handle, 1));
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPU_HW_D3D12::UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride)
|
||||
{
|
||||
#if 0
|
||||
if (!CreateTextureReplacementStreamBuffer())
|
||||
return;
|
||||
|
||||
const u32 copy_pitch = Common::AlignUpPow2<u32>(data_width * sizeof(u32), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
const u32 required_size = copy_pitch * data_height;
|
||||
if (!m_texture_replacment_stream_buffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
|
||||
{
|
||||
Log_PerfPrint("Executing command buffer while waiting for texture replacement buffer space");
|
||||
g_d3d12_context->ExecuteCommandList(false);
|
||||
RestoreGraphicsAPIState();
|
||||
if (!m_texture_replacment_stream_buffer.ReserveMemory(required_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to allocate %u bytes from texture replacement streaming buffer", required_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// buffer -> texture
|
||||
const u32 sb_offset = m_texture_replacment_stream_buffer.GetCurrentOffset();
|
||||
D3D12::Texture::CopyToUploadBuffer(data, data_stride, data_height,
|
||||
m_texture_replacment_stream_buffer.GetCurrentHostPointer(), copy_pitch);
|
||||
m_texture_replacment_stream_buffer.CommitMemory(required_size);
|
||||
m_texture_replacement_texture.CopyFromBuffer(page_index, page_x, page_y, data_width, data_height, copy_pitch,
|
||||
m_texture_replacment_stream_buffer.GetBuffer(), sb_offset);
|
||||
m_texture_replacement_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPU_HW_D3D12::InvalidateTextureReplacements()
|
||||
{
|
||||
if (!m_texture_replacement_texture)
|
||||
return;
|
||||
|
||||
static constexpr std::array<float, 4> clear_color = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
m_texture_replacement_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
g_d3d12_context->GetCommandList()->ClearRenderTargetView(m_texture_replacement_texture.GetRTVOrDSVDescriptor(),
|
||||
clear_color.data(), 0, nullptr);
|
||||
m_texture_replacement_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
}
|
||||
|
||||
void GPU_HW_D3D12::MapBatchVertexPointer(u32 required_vertices)
|
||||
{
|
||||
DebugAssert(!m_batch_start_vertex_ptr);
|
||||
|
@ -254,8 +413,14 @@ void GPU_HW_D3D12::DestroyResources()
|
|||
DestroyFramebuffer();
|
||||
DestroyPipelines();
|
||||
|
||||
m_texture_replacement_texture.Destroy(false);
|
||||
|
||||
g_d3d12_context->GetSamplerHeapManager().Free(&m_texture_replacement_sampler_handle,
|
||||
TEXTURE_REPLACEMENT_BATCH_TEXTURE_COUNT);
|
||||
g_d3d12_context->GetSamplerHeapManager().Free(&m_point_sampler);
|
||||
g_d3d12_context->GetSamplerHeapManager().Free(&m_linear_sampler);
|
||||
g_d3d12_context->GetDescriptorHeapManager().Free(&m_texture_replacement_srv_handle,
|
||||
TEXTURE_REPLACEMENT_BATCH_TEXTURE_COUNT);
|
||||
g_d3d12_context->GetDescriptorHeapManager().Free(&m_texture_stream_buffer_srv);
|
||||
|
||||
m_vertex_stream_buffer.Destroy(false);
|
||||
|
@ -263,6 +428,7 @@ void GPU_HW_D3D12::DestroyResources()
|
|||
m_texture_stream_buffer.Destroy(false);
|
||||
|
||||
m_single_sampler_root_signature.Reset();
|
||||
m_texture_replacement_root_signature.Reset();
|
||||
m_batch_root_signature.Reset();
|
||||
}
|
||||
|
||||
|
@ -277,6 +443,14 @@ bool GPU_HW_D3D12::CreateRootSignatures()
|
|||
if (!m_batch_root_signature)
|
||||
return false;
|
||||
|
||||
rsbuilder.SetInputAssemblerFlag();
|
||||
rsbuilder.AddCBVParameter(0, D3D12_SHADER_VISIBILITY_ALL);
|
||||
rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
m_texture_replacement_root_signature = rsbuilder.Create();
|
||||
if (!m_texture_replacement_root_signature)
|
||||
return false;
|
||||
|
||||
rsbuilder.Add32BitConstants(0, MAX_PUSH_CONSTANTS_SIZE / sizeof(u32), D3D12_SHADER_VISIBILITY_ALL);
|
||||
rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
rsbuilder.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, 1, D3D12_SHADER_VISIBILITY_PIXEL);
|
||||
|
@ -341,6 +515,8 @@ bool GPU_HW_D3D12::CreateFramebuffer()
|
|||
D3D12::SetObjectName(m_display_texture, "VRAM Display Texture");
|
||||
D3D12::SetObjectName(m_vram_read_texture, "VRAM Readback Texture");
|
||||
|
||||
UpdateTextureReplacementSRV();
|
||||
|
||||
m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||
m_vram_depth_texture.TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||
m_vram_read_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
|
@ -446,7 +622,7 @@ bool GPU_HW_D3D12::CompilePipelines()
|
|||
{
|
||||
const std::string fs = shadergen.GenerateBatchFragmentShader(
|
||||
static_cast<BatchRenderMode>(render_mode), static_cast<GPUTextureMode>(texture_mode),
|
||||
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing));
|
||||
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing), m_texture_replacements);
|
||||
|
||||
batch_fragment_shaders[render_mode][texture_mode][dithering][interlacing] = shader_cache.GetPixelShader(fs);
|
||||
if (!batch_fragment_shaders[render_mode][texture_mode][dithering][interlacing])
|
||||
|
@ -475,7 +651,8 @@ bool GPU_HW_D3D12::CompilePipelines()
|
|||
{
|
||||
const bool textured = (static_cast<GPUTextureMode>(texture_mode) != GPUTextureMode::Disabled);
|
||||
|
||||
gpbuilder.SetRootSignature(m_batch_root_signature.Get());
|
||||
gpbuilder.SetRootSignature(m_texture_replacements ? m_texture_replacement_root_signature.Get() :
|
||||
m_batch_root_signature.Get());
|
||||
gpbuilder.SetRenderTarget(0, m_vram_texture.GetFormat());
|
||||
gpbuilder.SetDepthStencilFormat(m_vram_depth_texture.GetFormat());
|
||||
|
||||
|
@ -1035,7 +1212,7 @@ void GPU_HW_D3D12::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* d
|
|||
const Common::Rectangle<u32> bounds = GetVRAMTransferBounds(x, y, width, height);
|
||||
GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask);
|
||||
|
||||
if (!check_mask)
|
||||
if (!check_mask && !IsFullVRAMUpload(x, y, width, height))
|
||||
{
|
||||
const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data);
|
||||
if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale,
|
||||
|
@ -1043,6 +1220,9 @@ void GPU_HW_D3D12::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* d
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_texture_replacements)
|
||||
g_texture_replacements.UploadReplacementTextures(x, y, width, height, data);
|
||||
}
|
||||
|
||||
const u32 data_size = width * height * sizeof(u16);
|
||||
|
@ -1175,6 +1355,9 @@ void GPU_HW_D3D12::UpdateDepthBufferFromMaskBit()
|
|||
|
||||
m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
|
||||
if (m_texture_replacements)
|
||||
cmdlist->SetGraphicsRootSignature(m_batch_root_signature.Get());
|
||||
|
||||
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, &m_vram_depth_texture.GetRTVOrDSVDescriptor().cpu_handle);
|
||||
cmdlist->SetGraphicsRootDescriptorTable(1, m_vram_texture.GetSRVDescriptor());
|
||||
cmdlist->SetPipelineState(m_vram_update_depth_pipeline.Get());
|
||||
|
|
|
@ -27,6 +27,10 @@ public:
|
|||
void RestoreGraphicsAPIState() override;
|
||||
void UpdateSettings() override;
|
||||
|
||||
void UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride) override;
|
||||
void InvalidateTextureReplacements() override;
|
||||
|
||||
protected:
|
||||
void ClearDisplay() override;
|
||||
void UpdateDisplay() override;
|
||||
|
@ -42,13 +46,18 @@ protected:
|
|||
void UnmapBatchVertexPointer(u32 used_vertices) override;
|
||||
void UploadUniformBuffer(const void* data, u32 data_size) override;
|
||||
void DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices) override;
|
||||
bool SetupTextureReplacementTexture() override;
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
{
|
||||
MAX_PUSH_CONSTANTS_SIZE = 64,
|
||||
TEXTURE_REPLACEMENT_BUFFER_SIZE = 64 * 1024 * 1024,
|
||||
TEXTURE_REPLACEMENT_BATCH_TEXTURE_COUNT = 2,
|
||||
};
|
||||
|
||||
static constexpr DXGI_FORMAT REPLACEMENT_TEXTURE_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
void SetCapabilities();
|
||||
void DestroyResources();
|
||||
|
||||
|
@ -68,8 +77,10 @@ private:
|
|||
|
||||
bool CreateTextureReplacementStreamBuffer();
|
||||
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||
void UpdateTextureReplacementSRV();
|
||||
|
||||
ComPtr<ID3D12RootSignature> m_batch_root_signature;
|
||||
ComPtr<ID3D12RootSignature> m_texture_replacement_root_signature;
|
||||
ComPtr<ID3D12RootSignature> m_single_sampler_root_signature;
|
||||
|
||||
D3D12::Texture m_vram_texture;
|
||||
|
@ -107,5 +118,8 @@ private:
|
|||
|
||||
ComPtr<ID3D12PipelineState> m_copy_pipeline;
|
||||
D3D12::Texture m_vram_write_replacement_texture;
|
||||
D3D12::Texture m_texture_replacement_texture;
|
||||
D3D12::StreamBuffer m_texture_replacment_stream_buffer;
|
||||
D3D12::DescriptorHandle m_texture_replacement_srv_handle;
|
||||
D3D12::DescriptorHandle m_texture_replacement_sampler_handle;
|
||||
};
|
||||
|
|
|
@ -232,6 +232,14 @@ void GPU_HW_OpenGL::RestoreGraphicsAPIState()
|
|||
glBindVertexArray(m_vao_id);
|
||||
m_uniform_stream_buffer->Bind();
|
||||
m_vram_read_texture.Bind();
|
||||
|
||||
if (m_texture_replacement_texture.IsValid())
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
m_texture_replacement_texture.Bind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
SetBlendMode();
|
||||
m_current_depth_test = 0;
|
||||
SetDepthFunc();
|
||||
|
@ -267,6 +275,79 @@ void GPU_HW_OpenGL::UpdateSettings()
|
|||
}
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::SetupTextureReplacementTexture()
|
||||
{
|
||||
const bool is_enabled = m_texture_replacement_texture.IsValid();
|
||||
if (is_enabled == m_texture_replacements)
|
||||
{
|
||||
if (!is_enabled ||
|
||||
(m_texture_replacement_texture.GetWidth() == g_texture_replacements.GetScaledReplacementTextureWidth() &&
|
||||
m_texture_replacement_texture.GetHeight() == g_texture_replacements.GetScaledReplacementTextureHeight()))
|
||||
{
|
||||
// no changes
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
m_texture_replacement_texture.Destroy();
|
||||
|
||||
if (m_texture_replacements)
|
||||
{
|
||||
const u32 width = g_texture_replacements.GetScaledReplacementTextureWidth();
|
||||
const u32 height = g_texture_replacements.GetScaledReplacementTextureHeight();
|
||||
if (!m_texture_replacement_texture.Create(width, height, TextureReplacements::TEXTURE_REPLACEMENT_PAGE_COUNT, 1, 1,
|
||||
GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, false, true))
|
||||
{
|
||||
m_texture_replacement_texture.Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
g_texture_replacements.ReuploadReplacementTextures();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride)
|
||||
{
|
||||
if (!m_texture_replacement_texture.IsValid())
|
||||
return;
|
||||
|
||||
// TODO: Get rid of this crap.
|
||||
std::vector<u32> temp;
|
||||
{
|
||||
temp.resize(data_width * data_height);
|
||||
|
||||
const u8* source_ptr = static_cast<const u8*>(data) + (data_stride * (data_height - 1));
|
||||
u8* dest_ptr = reinterpret_cast<u8*>(temp.data());
|
||||
const u32 copy_size = data_width * sizeof(u32);
|
||||
for (u32 row = 0; row < data_height; row++)
|
||||
{
|
||||
std::memcpy(dest_ptr, source_ptr, copy_size);
|
||||
dest_ptr += copy_size;
|
||||
source_ptr -= data_stride;
|
||||
}
|
||||
}
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, data_width);
|
||||
|
||||
m_texture_replacement_texture.Bind();
|
||||
m_texture_replacement_texture.ReplaceSubImage(page_index, 0, page_x,
|
||||
m_texture_replacement_texture.GetHeight() - page_y - data_height,
|
||||
data_width, data_height, GL_RGBA, GL_UNSIGNED_BYTE, temp.data());
|
||||
m_vram_read_texture.Bind();
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::InvalidateTextureReplacements()
|
||||
{
|
||||
m_texture_replacement_texture.Destroy();
|
||||
if (!SetupTextureReplacementTexture())
|
||||
Panic("Failed to re-create replacement textures for invalidation");
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::MapBatchVertexPointer(u32 required_vertices)
|
||||
{
|
||||
DebugAssert(!m_batch_start_vertex_ptr);
|
||||
|
@ -534,7 +615,7 @@ bool GPU_HW_OpenGL::CompilePrograms()
|
|||
const std::string batch_vs = shadergen.GenerateBatchVertexShader(textured);
|
||||
const std::string fs = shadergen.GenerateBatchFragmentShader(
|
||||
static_cast<BatchRenderMode>(render_mode), static_cast<GPUTextureMode>(texture_mode),
|
||||
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing));
|
||||
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing), m_texture_replacements);
|
||||
|
||||
const auto link_callback = [this, textured, use_binding_layout](GL::Program& prog) {
|
||||
if (!use_binding_layout)
|
||||
|
@ -1066,7 +1147,7 @@ void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void*
|
|||
const Common::Rectangle<u32> bounds = GetVRAMTransferBounds(x, y, width, height);
|
||||
GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask);
|
||||
|
||||
if (!check_mask)
|
||||
if (!check_mask && !IsFullVRAMUpload(x, y, width, height))
|
||||
{
|
||||
const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data);
|
||||
if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale,
|
||||
|
@ -1074,6 +1155,9 @@ void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void*
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_texture_replacements)
|
||||
g_texture_replacements.UploadReplacementTextures(x, y, width, height, data);
|
||||
}
|
||||
|
||||
const u32 num_pixels = width * height;
|
||||
|
|
|
@ -26,6 +26,10 @@ public:
|
|||
void RestoreGraphicsAPIState() override;
|
||||
void UpdateSettings() override;
|
||||
|
||||
void UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride) override;
|
||||
void InvalidateTextureReplacements();
|
||||
|
||||
protected:
|
||||
void ClearDisplay() override;
|
||||
void UpdateDisplay() override;
|
||||
|
@ -41,6 +45,7 @@ protected:
|
|||
void UnmapBatchVertexPointer(u32 used_vertices) override;
|
||||
void UploadUniformBuffer(const void* data, u32 data_size) override;
|
||||
void DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices) override;
|
||||
bool SetupTextureReplacementTexture() override;
|
||||
|
||||
private:
|
||||
struct GLStats
|
||||
|
@ -84,6 +89,7 @@ private:
|
|||
GL::Texture m_vram_encoding_texture;
|
||||
GL::Texture m_display_texture;
|
||||
GL::Texture m_vram_write_replacement_texture;
|
||||
GL::Texture m_texture_replacement_texture;
|
||||
|
||||
std::unique_ptr<GL::StreamBuffer> m_vertex_stream_buffer;
|
||||
GLuint m_vram_fbo_id = 0;
|
||||
|
|
|
@ -665,7 +665,8 @@ void FilteredSampleFromVRAM(uint4 texpage, float2 coords, float4 uv_limits,
|
|||
}
|
||||
|
||||
std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMode transparency,
|
||||
GPUTextureMode texture_mode, bool dithering, bool interlacing)
|
||||
GPUTextureMode texture_mode, bool dithering, bool interlacing,
|
||||
bool texture_replacement)
|
||||
{
|
||||
const GPUTextureMode actual_texture_mode = texture_mode & ~GPUTextureMode::RawTextureBit;
|
||||
const bool raw_texture = (texture_mode & GPUTextureMode::RawTextureBit) == GPUTextureMode::RawTextureBit;
|
||||
|
@ -694,11 +695,15 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(GPU_HW::BatchRenderMod
|
|||
DefineMacro(ss, "UV_LIMITS", m_uv_limits);
|
||||
DefineMacro(ss, "USE_DUAL_SOURCE", use_dual_source);
|
||||
DefineMacro(ss, "PGXP_DEPTH", m_pgxp_depth);
|
||||
DefineMacro(ss, "TEXTURE_REPLACEMENT", texture_replacement);
|
||||
|
||||
WriteCommonFunctions(ss);
|
||||
WriteBatchUniformBuffer(ss);
|
||||
DeclareTexture(ss, "samp0", 0);
|
||||
|
||||
if (texture_replacement)
|
||||
DeclareTextureArray(ss, "samp1", 1);
|
||||
|
||||
if (m_glsl)
|
||||
ss << "CONSTANT int[16] s_dither_values = int[16]( ";
|
||||
else
|
||||
|
@ -793,7 +798,32 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
|
|||
#endif
|
||||
}
|
||||
|
||||
#if TEXTURE_REPLACEMENT
|
||||
float4 SampleReplacementTexture(uint4 texpage, float2 coords)
|
||||
{
|
||||
uint2 replace_icoord = ApplyUpscaledTextureWindow(FloatToIntegerCoords(coords));
|
||||
float2 replace_ncoord = float2(replace_icoord) / float2(256u * RESOLUTION_SCALE, 256u * RESOLUTION_SCALE);
|
||||
uint replace_layer = ((texpage.y / (256u * RESOLUTION_SCALE)) * 16) + (texpage.x / (64u * RESOLUTION_SCALE));
|
||||
#if PALETTE_8_BIT
|
||||
uint extra_pages = (uint(coords.x) / (128u * RESOLUTION_SCALE));
|
||||
replace_layer += extra_pages;
|
||||
replace_ncoord.x -= float(extra_pages) * (128.0f / 256.0f);
|
||||
#elif !PALETTE
|
||||
uint extra_pages = (uint(coords.x) / (64u * RESOLUTION_SCALE));
|
||||
replace_layer += extra_pages;
|
||||
replace_ncoord.x -= float(extra_pages) * (64.0f / 256.0f);
|
||||
#endif
|
||||
|
||||
#if API_OPENGL || API_OPENGL_ES
|
||||
replace_ncoord.y = 1.0 - replace_ncoord.y;
|
||||
#endif
|
||||
|
||||
return SAMPLE_TEXTURE_ARRAY(samp1, replace_ncoord, float(replace_layer));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TEXTURED
|
||||
|
||||
)";
|
||||
|
||||
if (textured)
|
||||
|
@ -834,14 +864,6 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
|
|||
#endif
|
||||
|
||||
#if TEXTURED
|
||||
|
||||
// We can't currently use upscaled coordinate for palettes because of how they're packed.
|
||||
// Not that it would be any benefit anyway, render-to-texture effects don't use palettes.
|
||||
float2 coords = v_tex0;
|
||||
#if PALETTE
|
||||
coords /= float2(RESOLUTION_SCALE, RESOLUTION_SCALE);
|
||||
#endif
|
||||
|
||||
#if UV_LIMITS
|
||||
float4 uv_limits = v_uv_limits;
|
||||
#if !PALETTE
|
||||
|
@ -853,20 +875,41 @@ float4 SampleFromVRAM(uint4 texpage, float2 coords)
|
|||
#endif
|
||||
|
||||
float4 texcol;
|
||||
#if TEXTURE_FILTERING
|
||||
FilteredSampleFromVRAM(v_texpage, coords, uv_limits, texcol, ialpha);
|
||||
if (ialpha < 0.5)
|
||||
discard;
|
||||
#else
|
||||
#if UV_LIMITS
|
||||
texcol = SampleFromVRAM(v_texpage, clamp(coords, uv_limits.xy, uv_limits.zw));
|
||||
#else
|
||||
texcol = SampleFromVRAM(v_texpage, coords);
|
||||
#endif
|
||||
if (VECTOR_EQ(texcol, TRANSPARENT_PIXEL_COLOR))
|
||||
discard;
|
||||
|
||||
ialpha = 1.0;
|
||||
#if TEXTURE_REPLACEMENT
|
||||
// TODO: Do we want to apply UV limits here?
|
||||
texcol = SampleReplacementTexture(v_texpage, v_tex0);
|
||||
if (texcol.a >= 0.5)
|
||||
{
|
||||
ialpha = (texcol.a * 2.0) - 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
// We can't currently use upscaled coordinate for palettes because of how they're packed.
|
||||
// Not that it would be any benefit anyway, render-to-texture effects don't use palettes.
|
||||
float2 coords = v_tex0;
|
||||
#if PALETTE
|
||||
coords /= float2(RESOLUTION_SCALE, RESOLUTION_SCALE);
|
||||
#endif
|
||||
|
||||
#if TEXTURE_FILTERING
|
||||
FilteredSampleFromVRAM(v_texpage, coords, uv_limits, texcol, ialpha);
|
||||
if (ialpha < 0.5)
|
||||
discard;
|
||||
#else
|
||||
#if UV_LIMITS
|
||||
texcol = SampleFromVRAM(v_texpage, clamp(coords, uv_limits.xy, uv_limits.zw));
|
||||
#else
|
||||
texcol = SampleFromVRAM(v_texpage, coords);
|
||||
#endif
|
||||
if (VECTOR_EQ(texcol, TRANSPARENT_PIXEL_COLOR))
|
||||
discard;
|
||||
|
||||
ialpha = 1.0;
|
||||
#endif
|
||||
#if TEXTURE_REPLACEMENT
|
||||
}
|
||||
#endif
|
||||
|
||||
semitransparent = (texcol.a >= 0.5);
|
||||
|
|
|
@ -12,7 +12,7 @@ public:
|
|||
|
||||
std::string GenerateBatchVertexShader(bool textured);
|
||||
std::string GenerateBatchFragmentShader(GPU_HW::BatchRenderMode transparency, GPUTextureMode texture_mode,
|
||||
bool dithering, bool interlacing);
|
||||
bool dithering, bool interlacing, bool texture_replacement);
|
||||
std::string GenerateDisplayFragmentShader(bool depth_24bit, GPU_HW::InterlacedRenderMode interlace_mode,
|
||||
bool smooth_chroma);
|
||||
std::string GenerateVRAMReadFragmentShader();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/log.h"
|
||||
#include "common/scoped_guard.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/vulkan/builders.h"
|
||||
#include "common/vulkan/context.h"
|
||||
|
@ -197,8 +198,10 @@ void GPU_HW_Vulkan::RestoreGraphicsAPIState()
|
|||
VkDeviceSize vertex_buffer_offset = 0;
|
||||
vkCmdBindVertexBuffers(cmdbuf, 0, 1, m_vertex_stream_buffer.GetBufferPointer(), &vertex_buffer_offset);
|
||||
Vulkan::Util::SetViewport(cmdbuf, 0, 0, m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
|
||||
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_batch_pipeline_layout, 0, 1,
|
||||
&m_batch_descriptor_set, 1, &m_current_uniform_buffer_offset);
|
||||
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_texture_replacements ? m_texture_replacement_batch_pipeline_layout :
|
||||
m_batch_pipeline_layout,
|
||||
0, 1, &m_batch_descriptor_set, 1, &m_current_uniform_buffer_offset);
|
||||
SetScissorFromDrawingArea();
|
||||
}
|
||||
|
||||
|
@ -241,6 +244,83 @@ void GPU_HW_Vulkan::UpdateSettings()
|
|||
}
|
||||
}
|
||||
|
||||
bool GPU_HW_Vulkan::SetupTextureReplacementTexture()
|
||||
{
|
||||
const bool is_enabled = m_texture_replacement_texture.IsValid();
|
||||
if (is_enabled == m_texture_replacements)
|
||||
{
|
||||
if (!is_enabled ||
|
||||
(m_texture_replacement_texture.GetWidth() == g_texture_replacements.GetScaledReplacementTextureWidth() &&
|
||||
m_texture_replacement_texture.GetHeight() == g_texture_replacements.GetScaledReplacementTextureHeight()))
|
||||
{
|
||||
// no changes
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
g_vulkan_context->ExecuteCommandBuffer(true);
|
||||
m_texture_replacement_texture.Destroy(false);
|
||||
|
||||
if (m_texture_replacements)
|
||||
{
|
||||
const u32 width = g_texture_replacements.GetScaledReplacementTextureWidth();
|
||||
const u32 height = g_texture_replacements.GetScaledReplacementTextureHeight();
|
||||
if (!m_texture_replacement_texture.Create(width, height, 1, TextureReplacements::TEXTURE_REPLACEMENT_PAGE_COUNT,
|
||||
VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT,
|
||||
VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const VkClearColorValue cv = {};
|
||||
const VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0,
|
||||
m_texture_replacement_texture.GetLayers()};
|
||||
m_texture_replacement_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vkCmdClearColorImage(g_vulkan_context->GetCurrentCommandBuffer(), m_texture_replacement_texture.GetImage(),
|
||||
m_texture_replacement_texture.GetLayout(), &cv, 1, &range);
|
||||
m_texture_replacement_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
if (m_batch_descriptor_set != VK_NULL_HANDLE && !CreateBatchDesciptorSet(m_texture_replacements))
|
||||
{
|
||||
if (!CreateBatchDesciptorSet(false))
|
||||
Panic("Failed to create non-texture-replacement descriptor set");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_texture_replacements)
|
||||
g_texture_replacements.ReuploadReplacementTextures();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride)
|
||||
{
|
||||
// buffer -> texture
|
||||
EndRenderPass();
|
||||
m_texture_replacement_texture.Update(page_x, page_y, data_width, data_height, 0, page_index, data, data_stride);
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::InvalidateTextureReplacements()
|
||||
{
|
||||
if (!m_texture_replacement_texture.IsValid())
|
||||
return;
|
||||
|
||||
const VkClearColorValue cv = {};
|
||||
const VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, m_texture_replacement_texture.GetLayers()};
|
||||
m_texture_replacement_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vkCmdClearColorImage(g_vulkan_context->GetCurrentCommandBuffer(), m_texture_replacement_texture.GetImage(),
|
||||
m_texture_replacement_texture.GetLayout(), &cv, 1, &range);
|
||||
m_texture_replacement_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::MapBatchVertexPointer(u32 required_vertices)
|
||||
{
|
||||
DebugAssert(!m_batch_start_vertex_ptr);
|
||||
|
@ -287,7 +367,9 @@ void GPU_HW_Vulkan::UploadUniformBuffer(const void* data, u32 data_size)
|
|||
m_uniform_stream_buffer.CommitMemory(data_size);
|
||||
|
||||
vkCmdBindDescriptorSets(g_vulkan_context->GetCurrentCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_batch_pipeline_layout, 0, 1, &m_batch_descriptor_set, 1, &m_current_uniform_buffer_offset);
|
||||
m_texture_replacements ? m_texture_replacement_batch_pipeline_layout :
|
||||
m_batch_pipeline_layout,
|
||||
0, 1, &m_batch_descriptor_set, 1, &m_current_uniform_buffer_offset);
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::SetCapabilities()
|
||||
|
@ -369,9 +451,11 @@ void GPU_HW_Vulkan::DestroyResources()
|
|||
Vulkan::Util::SafeDestroyPipelineLayout(m_vram_write_pipeline_layout);
|
||||
Vulkan::Util::SafeDestroyPipelineLayout(m_single_sampler_pipeline_layout);
|
||||
Vulkan::Util::SafeDestroyPipelineLayout(m_no_samplers_pipeline_layout);
|
||||
Vulkan::Util::SafeDestroyPipelineLayout(m_texture_replacement_batch_pipeline_layout);
|
||||
Vulkan::Util::SafeDestroyPipelineLayout(m_batch_pipeline_layout);
|
||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_vram_write_descriptor_set_layout);
|
||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_single_sampler_descriptor_set_layout);
|
||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_texture_replacement_batch_descriptor_set_layout);
|
||||
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_batch_descriptor_set_layout);
|
||||
Vulkan::Util::SafeDestroySampler(m_point_sampler);
|
||||
Vulkan::Util::SafeDestroySampler(m_linear_sampler);
|
||||
|
@ -438,6 +522,17 @@ bool GPU_HW_Vulkan::CreatePipelineLayouts()
|
|||
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_batch_descriptor_set_layout,
|
||||
"Batch Descriptor Set Layout");
|
||||
|
||||
dslbuilder.AddBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1,
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
dslbuilder.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
dslbuilder.AddBinding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
m_texture_replacement_batch_descriptor_set_layout = dslbuilder.Create(device);
|
||||
if (m_texture_replacement_batch_descriptor_set_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_texture_replacement_batch_descriptor_set_layout,
|
||||
"Batch Replacement Descriptor Set Layout");
|
||||
|
||||
// textures start at 1
|
||||
dslbuilder.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
m_single_sampler_descriptor_set_layout = dslbuilder.Create(device);
|
||||
|
@ -463,6 +558,11 @@ bool GPU_HW_Vulkan::CreatePipelineLayouts()
|
|||
return false;
|
||||
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), m_batch_pipeline_layout, "Batch Pipeline Layout");
|
||||
|
||||
plbuilder.AddDescriptorSet(m_texture_replacement_batch_descriptor_set_layout);
|
||||
m_texture_replacement_batch_pipeline_layout = plbuilder.Create(device);
|
||||
if (m_texture_replacement_batch_pipeline_layout == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
plbuilder.AddDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
plbuilder.AddPushConstants(VK_SHADER_STAGE_FRAGMENT_BIT, 0, MAX_PUSH_CONSTANTS_SIZE);
|
||||
m_single_sampler_pipeline_layout = plbuilder.Create(device);
|
||||
|
@ -663,22 +763,20 @@ bool GPU_HW_Vulkan::CreateFramebuffer()
|
|||
m_vram_depth_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||
m_vram_read_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
if (!CreateBatchDesciptorSet(m_texture_replacements))
|
||||
return false;
|
||||
|
||||
Vulkan::DescriptorSetUpdateBuilder dsubuilder;
|
||||
|
||||
m_batch_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_batch_descriptor_set_layout);
|
||||
m_vram_copy_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
m_vram_read_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
m_display_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(m_single_sampler_descriptor_set_layout);
|
||||
if (m_batch_descriptor_set == VK_NULL_HANDLE || m_vram_copy_descriptor_set == VK_NULL_HANDLE ||
|
||||
m_vram_read_descriptor_set == VK_NULL_HANDLE || m_display_descriptor_set == VK_NULL_HANDLE)
|
||||
if (m_vram_copy_descriptor_set == VK_NULL_HANDLE || m_vram_read_descriptor_set == VK_NULL_HANDLE ||
|
||||
m_display_descriptor_set == VK_NULL_HANDLE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dsubuilder.AddBufferDescriptorWrite(m_batch_descriptor_set, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
m_uniform_stream_buffer.GetBuffer(), 0, sizeof(BatchUBOData));
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_batch_descriptor_set, 1, m_vram_read_texture.GetView(),
|
||||
m_point_sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_vram_copy_descriptor_set, 1, m_vram_read_texture.GetView(),
|
||||
m_point_sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_vram_read_descriptor_set, 1, m_vram_texture.GetView(),
|
||||
|
@ -790,6 +888,33 @@ bool GPU_HW_Vulkan::CreateFramebuffer()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GPU_HW_Vulkan::CreateBatchDesciptorSet(bool texture_replacements)
|
||||
{
|
||||
if (m_batch_descriptor_set != VK_NULL_HANDLE)
|
||||
g_vulkan_context->FreeGlobalDescriptorSet(m_batch_descriptor_set);
|
||||
|
||||
m_batch_descriptor_set = g_vulkan_context->AllocateGlobalDescriptorSet(
|
||||
texture_replacements ? m_texture_replacement_batch_descriptor_set_layout : m_batch_descriptor_set_layout);
|
||||
if (m_batch_descriptor_set == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
Vulkan::DescriptorSetUpdateBuilder dsubuilder;
|
||||
dsubuilder.AddBufferDescriptorWrite(m_batch_descriptor_set, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
m_uniform_stream_buffer.GetBuffer(), 0, sizeof(BatchUBOData));
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_batch_descriptor_set, 1, m_vram_read_texture.GetView(),
|
||||
m_point_sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
if (texture_replacements)
|
||||
{
|
||||
Assert(m_texture_replacement_texture.IsValid());
|
||||
dsubuilder.AddCombinedImageSamplerDescriptorWrite(m_batch_descriptor_set, 2,
|
||||
m_texture_replacement_texture.GetView(), m_point_sampler,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
|
||||
dsubuilder.Update(g_vulkan_context->GetDevice());
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_Vulkan::ClearFramebuffer()
|
||||
{
|
||||
VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
|
@ -958,7 +1083,7 @@ bool GPU_HW_Vulkan::CompilePipelines()
|
|||
{
|
||||
const std::string fs = shadergen.GenerateBatchFragmentShader(
|
||||
static_cast<BatchRenderMode>(render_mode), static_cast<GPUTextureMode>(texture_mode),
|
||||
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing));
|
||||
ConvertToBoolUnchecked(dithering), ConvertToBoolUnchecked(interlacing), m_texture_replacements);
|
||||
|
||||
VkShaderModule shader = g_vulkan_shader_cache->GetFragmentShader(fs);
|
||||
if (shader == VK_NULL_HANDLE)
|
||||
|
@ -990,7 +1115,8 @@ bool GPU_HW_Vulkan::CompilePipelines()
|
|||
VK_COMPARE_OP_ALWAYS, VK_COMPARE_OP_GREATER_OR_EQUAL, VK_COMPARE_OP_LESS_OR_EQUAL};
|
||||
const bool textured = (static_cast<GPUTextureMode>(texture_mode) != GPUTextureMode::Disabled);
|
||||
|
||||
gpbuilder.SetPipelineLayout(m_batch_pipeline_layout);
|
||||
gpbuilder.SetPipelineLayout(m_texture_replacements ? m_texture_replacement_batch_pipeline_layout :
|
||||
m_batch_pipeline_layout);
|
||||
gpbuilder.SetRenderPass(m_vram_render_pass, 0);
|
||||
|
||||
gpbuilder.AddVertexBuffer(0, sizeof(BatchVertex), VK_VERTEX_INPUT_RATE_VERTEX);
|
||||
|
@ -1618,7 +1744,7 @@ void GPU_HW_Vulkan::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void*
|
|||
const Common::Rectangle<u32> bounds = GetVRAMTransferBounds(x, y, width, height);
|
||||
GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask);
|
||||
|
||||
if (!check_mask)
|
||||
if (!check_mask && !IsFullVRAMUpload(x, y, width, height))
|
||||
{
|
||||
const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data);
|
||||
if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale,
|
||||
|
@ -1626,6 +1752,9 @@ void GPU_HW_Vulkan::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void*
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_texture_replacements)
|
||||
g_texture_replacements.UploadReplacementTextures(x, y, width, height, data);
|
||||
}
|
||||
|
||||
const u32 data_size = width * height * sizeof(u16);
|
||||
|
|
|
@ -24,6 +24,10 @@ public:
|
|||
void RestoreGraphicsAPIState() override;
|
||||
void UpdateSettings() override;
|
||||
|
||||
void UploadTextureReplacement(u32 page_index, u32 page_x, u32 page_y, u32 data_width, u32 data_height,
|
||||
const void* data, u32 data_stride) override;
|
||||
void InvalidateTextureReplacements() override;
|
||||
|
||||
protected:
|
||||
void ClearDisplay() override;
|
||||
void UpdateDisplay() override;
|
||||
|
@ -39,6 +43,7 @@ protected:
|
|||
void UnmapBatchVertexPointer(u32 used_vertices) override;
|
||||
void UploadUniformBuffer(const void* data, u32 data_size) override;
|
||||
void DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices) override;
|
||||
bool SetupTextureReplacementTexture() override;
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
|
@ -59,6 +64,7 @@ private:
|
|||
bool CreateSamplers();
|
||||
|
||||
bool CreateFramebuffer();
|
||||
bool CreateBatchDesciptorSet(bool texture_replacements);
|
||||
void ClearFramebuffer();
|
||||
void DestroyFramebuffer();
|
||||
|
||||
|
@ -84,10 +90,12 @@ private:
|
|||
VkRenderPass m_vram_readback_render_pass = VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorSetLayout m_batch_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_texture_replacement_batch_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_single_sampler_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_vram_write_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
|
||||
VkPipelineLayout m_batch_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_texture_replacement_batch_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_no_samplers_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_single_sampler_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_vram_write_pipeline_layout = VK_NULL_HANDLE;
|
||||
|
@ -139,6 +147,7 @@ private:
|
|||
|
||||
// texture replacements
|
||||
Vulkan::Texture m_vram_write_replacement_texture;
|
||||
Vulkan::Texture m_texture_replacement_texture;
|
||||
|
||||
// downsampling
|
||||
Vulkan::Texture m_downsample_texture;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -369,14 +369,37 @@ void Settings::Load(SettingsInterface& si)
|
|||
|
||||
texture_replacements.enable_vram_write_replacements =
|
||||
si.GetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements", false);
|
||||
texture_replacements.enable_texture_replacements =
|
||||
si.GetBoolValue("TextureReplacements", "EnableTextureReplacements", false);
|
||||
texture_replacements.preload_textures = si.GetBoolValue("TextureReplacements", "PreloadTextures", false);
|
||||
texture_replacements.replacement_texture_scale =
|
||||
static_cast<u32>(si.GetIntValue("TextureReplacements", "TextureReplacementScale", 0));
|
||||
if (texture_replacements.replacement_texture_scale == 0)
|
||||
texture_replacements.replacement_texture_scale = gpu_resolution_scale;
|
||||
|
||||
texture_replacements.dump_vram_writes = si.GetBoolValue("TextureReplacements", "DumpVRAMWrites", false);
|
||||
texture_replacements.dump_vram_write_force_alpha_channel =
|
||||
si.GetBoolValue("TextureReplacements", "DumpVRAMWriteForceAlphaChannel", true);
|
||||
texture_replacements.dump_vram_write_width_threshold =
|
||||
si.GetIntValue("TextureReplacements", "DumpVRAMWriteWidthThreshold", 128);
|
||||
si.GetIntValue("TextureReplacements", "DumpVRAMWriteWidthThreshold", DEFAULT_VRAM_WRITE_DUMP_WIDTH_THRESHOLD);
|
||||
texture_replacements.dump_vram_write_height_threshold =
|
||||
si.GetIntValue("TextureReplacements", "DumpVRAMWriteHeightThreshold", 128);
|
||||
si.GetIntValue("TextureReplacements", "DumpVRAMWriteHeightThreshold", DEFAULT_VRAM_WRITE_DUMP_HEIGHT_THRESHOLD);
|
||||
|
||||
texture_replacements.dump_textures_by_vram_write =
|
||||
si.GetBoolValue("TextureReplacements", "DumpTexturesByVRAMWrite", false);
|
||||
texture_replacements.dump_textures_by_palette =
|
||||
si.GetBoolValue("TextureReplacements", "DumpTexturesByPalette", false);
|
||||
texture_replacements.dump_textures_force_alpha_channel =
|
||||
si.GetBoolValue("TextureReplacements", "DumpTexturesForceAlphaChannel", false);
|
||||
texture_replacements.dump_textures_max_merge_width =
|
||||
si.GetIntValue("TextureReplacements", "DumpTexturesMaxMergeWidth", DEFAULT_TEXTURE_DUMP_MAX_MERGE_WIDTH);
|
||||
texture_replacements.dump_textures_max_merge_height =
|
||||
si.GetIntValue("TextureReplacements", "DumpTexturesMaxMergeHeight", DEFAULT_TEXTURE_DUMP_MAX_MERGE_HEIGHT);
|
||||
texture_replacements.dump_textures_max_mergee_width =
|
||||
si.GetIntValue("TextureReplacements", "DumpTexturesMaxMergeeWidth", DEFAULT_TEXTURE_DUMP_MAX_MERGEE_WIDTH);
|
||||
texture_replacements.dump_textures_max_mergee_height =
|
||||
si.GetIntValue("TextureReplacements", "DumpTexturesMaxMergeeHeight", DEFAULT_TEXTURE_DUMP_MAX_MERGEE_HEIGHT);
|
||||
texture_replacements.UpdateTextureDumpingEnabled();
|
||||
}
|
||||
|
||||
void Settings::Save(SettingsInterface& si) const
|
||||
|
@ -552,7 +575,10 @@ void Settings::Save(SettingsInterface& si) const
|
|||
|
||||
si.SetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements",
|
||||
texture_replacements.enable_vram_write_replacements);
|
||||
si.SetBoolValue("TextureReplacements", "EnableTextureReplacements", texture_replacements.enable_texture_replacements);
|
||||
si.SetBoolValue("TextureReplacements", "PreloadTextures", texture_replacements.preload_textures);
|
||||
si.SetIntValue("TextureReplacements", "TextureReplacementScale", texture_replacements.replacement_texture_scale);
|
||||
|
||||
si.SetBoolValue("TextureReplacements", "DumpVRAMWrites", texture_replacements.dump_vram_writes);
|
||||
si.SetBoolValue("TextureReplacements", "DumpVRAMWriteForceAlphaChannel",
|
||||
texture_replacements.dump_vram_write_force_alpha_channel);
|
||||
|
@ -560,6 +586,19 @@ void Settings::Save(SettingsInterface& si) const
|
|||
texture_replacements.dump_vram_write_width_threshold);
|
||||
si.SetIntValue("TextureReplacements", "DumpVRAMWriteHeightThreshold",
|
||||
texture_replacements.dump_vram_write_height_threshold);
|
||||
|
||||
si.SetBoolValue("TextureReplacements", "DumpTexturesByVRAMWrite", texture_replacements.dump_textures_by_vram_write);
|
||||
si.SetBoolValue("TextureReplacements", "DumpTexturesByPalette", texture_replacements.dump_textures_by_palette);
|
||||
si.SetBoolValue("TextureReplacements", "DumpTexturesForceAlphaChannel",
|
||||
texture_replacements.dump_textures_force_alpha_channel);
|
||||
si.SetIntValue("TextureReplacements", "DumpTexturesMaxMergeWidth",
|
||||
texture_replacements.dump_textures_max_merge_width);
|
||||
si.SetIntValue("TextureReplacements", "DumpTexturesMaxMergeHeight",
|
||||
texture_replacements.dump_textures_max_merge_height);
|
||||
si.SetIntValue("TextureReplacements", "DumpTexturesMaxMergeeWidth",
|
||||
texture_replacements.dump_textures_max_mergee_width);
|
||||
si.SetIntValue("TextureReplacements", "DumpTexturesMaxMergeeHeight",
|
||||
texture_replacements.dump_textures_max_mergee_height);
|
||||
}
|
||||
|
||||
void Settings::FixIncompatibleSettings(bool display_osd_messages)
|
||||
|
@ -632,6 +671,13 @@ void Settings::FixIncompatibleSettings(bool display_osd_messages)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (g_settings.IsUsingSoftwareRenderer() && g_settings.texture_replacements.IsAnyDumpingEnabled())
|
||||
{
|
||||
Log_WarningPrintf("Disabling texture dumping because of software renderer");
|
||||
g_settings.texture_replacements.dump_vram_writes = false;
|
||||
g_settings.texture_replacements.dump_textures = false;
|
||||
}
|
||||
|
||||
// if challenge mode is enabled, disable things like rewind since they use save states
|
||||
if (Achievements::ChallengeModeActive())
|
||||
{
|
||||
|
|
|
@ -201,18 +201,35 @@ struct Settings
|
|||
struct TextureReplacementSettings
|
||||
{
|
||||
bool enable_vram_write_replacements = false;
|
||||
bool enable_texture_replacements = false;
|
||||
bool preload_textures = false;
|
||||
|
||||
bool dump_vram_writes = false;
|
||||
bool dump_vram_write_force_alpha_channel = true;
|
||||
u32 dump_vram_write_width_threshold = 128;
|
||||
u32 dump_vram_write_height_threshold = 128;
|
||||
|
||||
ALWAYS_INLINE bool AnyReplacementsEnabled() const { return enable_vram_write_replacements; }
|
||||
bool dump_textures = false;
|
||||
bool dump_textures_by_vram_write = false;
|
||||
bool dump_textures_by_palette = false;
|
||||
bool dump_textures_force_alpha_channel = false;
|
||||
|
||||
ALWAYS_INLINE bool ShouldDumpVRAMWrite(u32 width, u32 height)
|
||||
u32 replacement_texture_scale = 0;
|
||||
|
||||
u32 dump_vram_write_width_threshold = DEFAULT_VRAM_WRITE_DUMP_WIDTH_THRESHOLD;
|
||||
u32 dump_vram_write_height_threshold = DEFAULT_VRAM_WRITE_DUMP_HEIGHT_THRESHOLD;
|
||||
|
||||
u32 dump_textures_max_merge_width = DEFAULT_TEXTURE_DUMP_MAX_MERGE_WIDTH;
|
||||
u32 dump_textures_max_merge_height = DEFAULT_TEXTURE_DUMP_MAX_MERGE_HEIGHT;
|
||||
u32 dump_textures_max_mergee_width = DEFAULT_TEXTURE_DUMP_MAX_MERGEE_WIDTH;
|
||||
u32 dump_textures_max_mergee_height = DEFAULT_TEXTURE_DUMP_MAX_MERGEE_HEIGHT;
|
||||
|
||||
ALWAYS_INLINE bool AnyReplacementsEnabled() const
|
||||
{
|
||||
return dump_vram_writes && width >= dump_vram_write_width_threshold && height >= dump_vram_write_height_threshold;
|
||||
return enable_vram_write_replacements || enable_texture_replacements;
|
||||
}
|
||||
ALWAYS_INLINE bool IsAnyDumpingEnabled() const { return dump_vram_writes || dump_textures; }
|
||||
ALWAYS_INLINE void UpdateTextureDumpingEnabled()
|
||||
{
|
||||
dump_textures = dump_textures_by_vram_write || dump_textures_by_palette;
|
||||
}
|
||||
} texture_replacements;
|
||||
|
||||
|
@ -310,6 +327,10 @@ struct Settings
|
|||
DEFAULT_GPU_MAX_RUN_AHEAD = 128,
|
||||
DEFAULT_VRAM_WRITE_DUMP_WIDTH_THRESHOLD = 128,
|
||||
DEFAULT_VRAM_WRITE_DUMP_HEIGHT_THRESHOLD = 128,
|
||||
DEFAULT_TEXTURE_DUMP_MAX_MERGE_WIDTH = 256,
|
||||
DEFAULT_TEXTURE_DUMP_MAX_MERGE_HEIGHT = 256,
|
||||
DEFAULT_TEXTURE_DUMP_MAX_MERGEE_WIDTH = 256,
|
||||
DEFAULT_TEXTURE_DUMP_MAX_MERGEE_HEIGHT = 1,
|
||||
};
|
||||
|
||||
void Load(SettingsInterface& si);
|
||||
|
|
|
@ -207,8 +207,10 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
|
|||
ss << "#define SAMPLE_TEXTURE_OFFSET(name, coords, offset) textureOffset(name, coords, offset)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_LEVEL(name, coords, level) textureLod(name, coords, level)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_LEVEL_OFFSET(name, coords, level, offset) textureLod(name, coords, level, offset)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_ARRAY(name, coords, layer) texture(name, float3((coords), (layer)))\n";
|
||||
ss << "#define LOAD_TEXTURE(name, coords, mip) texelFetch(name, coords, mip)\n";
|
||||
ss << "#define LOAD_TEXTURE_MS(name, coords, sample) texelFetch(name, coords, int(sample))\n";
|
||||
ss << "#define LOAD_TEXTURE_ARRAY(name, coords, layer, mip) texelFetch(name, int3((coords), (layer)), (mip))\n";
|
||||
ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) texelFetchOffset(name, coords, mip, offset)\n";
|
||||
ss << "#define LOAD_TEXTURE_BUFFER(name, index) texelFetch(name, index)\n";
|
||||
ss << "#define BEGIN_ARRAY(type, size) type[size](\n";
|
||||
|
@ -253,8 +255,10 @@ void ShaderGen::WriteHeader(std::stringstream& ss)
|
|||
ss << "#define SAMPLE_TEXTURE_LEVEL(name, coords, level) name.SampleLevel(name##_ss, coords, level)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_LEVEL_OFFSET(name, coords, level, offset) name.SampleLevel(name##_ss, coords, level, "
|
||||
"offset)\n";
|
||||
ss << "#define SAMPLE_TEXTURE_ARRAY(name, coords, layer) name.Sample(name##_ss, float3((coords), (layer)))\n";
|
||||
ss << "#define LOAD_TEXTURE(name, coords, mip) name.Load(int3(coords, mip))\n";
|
||||
ss << "#define LOAD_TEXTURE_MS(name, coords, sample) name.Load(coords, sample)\n";
|
||||
ss << "#define LOAD_TEXTURE_ARRAY(name, coords, layer, mip) name.Load(int4((coords), (layer), (mip)))\n";
|
||||
ss << "#define LOAD_TEXTURE_OFFSET(name, coords, mip, offset) name.Load(int3(coords, mip), offset)\n";
|
||||
ss << "#define LOAD_TEXTURE_BUFFER(name, index) name.Load(index)\n";
|
||||
ss << "#define BEGIN_ARRAY(type, size) {\n";
|
||||
|
@ -315,6 +319,24 @@ void ShaderGen::DeclareTexture(std::stringstream& ss, const char* name, u32 inde
|
|||
}
|
||||
}
|
||||
|
||||
void ShaderGen::DeclareTextureArray(std::stringstream& ss, const char* name, u32 index)
|
||||
{
|
||||
if (m_glsl)
|
||||
{
|
||||
if (IsVulkan())
|
||||
ss << "layout(set = 0, binding = " << (index + 1u) << ") ";
|
||||
else if (m_use_glsl_binding_layout)
|
||||
ss << "layout(binding = " << index << ") ";
|
||||
|
||||
ss << "uniform sampler2DArray " << name << ";\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "Texture2DArray " << name << " : register(t" << index << ");\n";
|
||||
ss << "SamplerState " << name << "_ss : register(s" << index << ");\n";
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGen::DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int, bool is_unsigned)
|
||||
{
|
||||
if (m_glsl)
|
||||
|
|
|
@ -34,6 +34,7 @@ protected:
|
|||
void DeclareUniformBuffer(std::stringstream& ss, const std::initializer_list<const char*>& members,
|
||||
bool push_constant_on_vulkan);
|
||||
void DeclareTexture(std::stringstream& ss, const char* name, u32 index, bool multisampled = false);
|
||||
void DeclareTextureArray(std::stringstream& ss, const char* name, u32 index);
|
||||
void DeclareTextureBuffer(std::stringstream& ss, const char* name, u32 index, bool is_int, bool is_unsigned);
|
||||
void DeclareVertexEntryPoint(std::stringstream& ss, const std::initializer_list<const char*>& attributes,
|
||||
u32 num_color_outputs, u32 num_texcoord_outputs,
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "save_state_version.h"
|
||||
#include "sio.h"
|
||||
#include "spu.h"
|
||||
#include "texture_dumper.h"
|
||||
#include "texture_replacements.h"
|
||||
#include "timers.h"
|
||||
#include "util/audio_stream.h"
|
||||
|
@ -1372,6 +1373,9 @@ bool System::Initialize(bool force_software_renderer)
|
|||
|
||||
UpdateThrottlePeriod();
|
||||
UpdateMemorySaveStateSettings();
|
||||
|
||||
g_texture_replacements.Reload();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1386,6 +1390,7 @@ void System::DestroySystem()
|
|||
|
||||
ClearMemorySaveStates();
|
||||
|
||||
TextureDumper::Shutdown();
|
||||
g_texture_replacements.Shutdown();
|
||||
|
||||
g_sio.Shutdown();
|
||||
|
@ -1660,6 +1665,15 @@ bool System::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool u
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: What do we want to do with runahead here?
|
||||
if (sw.IsReading())
|
||||
{
|
||||
g_texture_replacements.OnSystemReset();
|
||||
|
||||
if (g_settings.texture_replacements.IsAnyDumpingEnabled())
|
||||
TextureDumper::ClearState();
|
||||
}
|
||||
|
||||
return !sw.HasError();
|
||||
}
|
||||
|
||||
|
@ -1688,13 +1702,19 @@ void System::InternalReset()
|
|||
s_frame_number = 1;
|
||||
s_internal_frame_number = 0;
|
||||
TimingEvents::Reset();
|
||||
ResetPerformanceCounters();
|
||||
|
||||
g_texture_replacements.OnSystemReset();
|
||||
|
||||
if (g_settings.texture_replacements.IsAnyDumpingEnabled())
|
||||
TextureDumper::ClearState();
|
||||
|
||||
#ifdef WITH_CHEEVOS
|
||||
Achievements::ResetRuntime();
|
||||
#endif
|
||||
|
||||
g_gpu->ResetGraphicsAPIState();
|
||||
|
||||
ResetPerformanceCounters();
|
||||
}
|
||||
|
||||
std::string System::GetMediaPathFromSaveState(const char* path)
|
||||
|
@ -2910,7 +2930,7 @@ void System::UpdateRunningGame(const char* path, CDImage* image, bool booting)
|
|||
}
|
||||
}
|
||||
|
||||
g_texture_replacements.SetGameID(s_running_game_code);
|
||||
g_texture_replacements.Reload();
|
||||
|
||||
s_cheat_list.reset();
|
||||
if (g_settings.auto_load_cheats && !Achievements::ChallengeModeActive())
|
||||
|
@ -3165,7 +3185,12 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
|||
g_settings.display_line_start_offset != old_settings.display_line_start_offset ||
|
||||
g_settings.display_line_end_offset != old_settings.display_line_end_offset ||
|
||||
g_settings.rewind_enable != old_settings.rewind_enable ||
|
||||
g_settings.runahead_frames != old_settings.runahead_frames)
|
||||
g_settings.runahead_frames != old_settings.runahead_frames ||
|
||||
g_settings.texture_replacements.enable_texture_replacements !=
|
||||
old_settings.texture_replacements.enable_texture_replacements ||
|
||||
(g_settings.texture_replacements.enable_texture_replacements &&
|
||||
g_settings.texture_replacements.replacement_texture_scale !=
|
||||
old_settings.texture_replacements.replacement_texture_scale))
|
||||
{
|
||||
g_gpu->UpdateSettings();
|
||||
Host::InvalidateDisplay();
|
||||
|
@ -3223,11 +3248,19 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
|||
|
||||
if (g_settings.texture_replacements.enable_vram_write_replacements !=
|
||||
old_settings.texture_replacements.enable_vram_write_replacements ||
|
||||
g_settings.texture_replacements.enable_texture_replacements !=
|
||||
old_settings.texture_replacements.enable_texture_replacements ||
|
||||
g_settings.texture_replacements.preload_textures != old_settings.texture_replacements.preload_textures)
|
||||
{
|
||||
g_texture_replacements.Reload();
|
||||
}
|
||||
|
||||
if (g_settings.texture_replacements.IsAnyDumpingEnabled() !=
|
||||
old_settings.texture_replacements.IsAnyDumpingEnabled())
|
||||
{
|
||||
TextureDumper::ClearState();
|
||||
}
|
||||
|
||||
g_dma.SetMaxSliceTicks(g_settings.dma_max_slice_ticks);
|
||||
g_dma.SetHaltTicks(g_settings.dma_halt_ticks);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include "common/hash_combine.h"
|
||||
#include "common/image.h"
|
||||
#include "common/rectangle.h"
|
||||
#include "gpu_types.h"
|
||||
#include "types.h"
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace TextureDumper {
|
||||
std::string GetDumpDirectory();
|
||||
|
||||
void ClearState();
|
||||
void Shutdown();
|
||||
|
||||
void AddClear(u32 x, u32 y, u32 width, u32 height);
|
||||
void AddVRAMWrite(u32 x, u32 y, u32 width, u32 height, const void* pixels);
|
||||
void AddDraw(u16 draw_mode, u16 palette, u32 min_uv_x, u32 min_uv_y, u32 max_uv_x, u32 max_uv_y, bool transparent);
|
||||
} // namespace TextureDumper
|
|
@ -6,8 +6,10 @@
|
|||
#include "common/string_util.h"
|
||||
#include "common/timer.h"
|
||||
#include "fmt/format.h"
|
||||
#include "gpu_hw.h"
|
||||
#include "host.h"
|
||||
#include "settings.h"
|
||||
#include "system.h"
|
||||
#include "xxhash.h"
|
||||
#if defined(CPU_X86) || defined(CPU_X64)
|
||||
#include "xxh_x86dispatch.h"
|
||||
|
@ -17,25 +19,24 @@ Log_SetChannel(TextureReplacements);
|
|||
|
||||
TextureReplacements g_texture_replacements;
|
||||
|
||||
static constexpr u32 VRAMRGBA5551ToRGBA8888(u16 color)
|
||||
static GPU_HW* GetHWGPU()
|
||||
{
|
||||
u8 r = Truncate8(color & 31);
|
||||
u8 g = Truncate8((color >> 5) & 31);
|
||||
u8 b = Truncate8((color >> 10) & 31);
|
||||
u8 a = Truncate8((color >> 15) & 1);
|
||||
return static_cast<GPU_HW*>(g_gpu.get());
|
||||
}
|
||||
|
||||
// 00012345 -> 1234545
|
||||
b = (b << 3) | (b & 0b111);
|
||||
g = (g << 3) | (g & 0b111);
|
||||
r = (r << 3) | (r & 0b111);
|
||||
a = a ? 255 : 0;
|
||||
u32 TextureReplacements::GetScaledReplacementTextureWidth()
|
||||
{
|
||||
return TEXTURE_REPLACEMENT_PAGE_WIDTH * g_settings.texture_replacements.replacement_texture_scale;
|
||||
}
|
||||
|
||||
return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24);
|
||||
u32 TextureReplacements::GetScaledReplacementTextureHeight()
|
||||
{
|
||||
return TEXTURE_REPLACEMENT_PAGE_HEIGHT * g_settings.texture_replacements.replacement_texture_scale;
|
||||
}
|
||||
|
||||
std::string TextureReplacementHash::ToString() const
|
||||
{
|
||||
return StringUtil::StdStringFromFormat("%" PRIx64 "%" PRIx64, high, low);
|
||||
return StringUtil::StdStringFromFormat("%016" PRIx64 "%016" PRIx64, high, low);
|
||||
}
|
||||
|
||||
bool TextureReplacementHash::ParseString(const std::string_view& sv)
|
||||
|
@ -57,13 +58,25 @@ TextureReplacements::TextureReplacements() = default;
|
|||
|
||||
TextureReplacements::~TextureReplacements() = default;
|
||||
|
||||
void TextureReplacements::SetGameID(std::string game_id)
|
||||
void TextureReplacements::InvalidateReplacementTextures()
|
||||
{
|
||||
if (m_game_id == game_id)
|
||||
return;
|
||||
Assert(g_settings.texture_replacements.enable_texture_replacements);
|
||||
|
||||
m_game_id = game_id;
|
||||
Reload();
|
||||
// TODO: Clear cached textures.
|
||||
|
||||
if (GetHWGPU()->IsTextureReplacementEnabled())
|
||||
GetHWGPU()->InvalidateTextureReplacements();
|
||||
}
|
||||
|
||||
void TextureReplacements::ReuploadReplacementTextures()
|
||||
{
|
||||
Assert(g_settings.texture_replacements.enable_texture_replacements);
|
||||
}
|
||||
|
||||
void TextureReplacements::OnSystemReset()
|
||||
{
|
||||
if (g_settings.texture_replacements.enable_texture_replacements)
|
||||
InvalidateReplacementTextures();
|
||||
}
|
||||
|
||||
const TextureReplacementTexture* TextureReplacements::GetVRAMWriteReplacement(u32 width, u32 height, const void* pixels)
|
||||
|
@ -77,84 +90,213 @@ const TextureReplacementTexture* TextureReplacements::GetVRAMWriteReplacement(u3
|
|||
return LoadTexture(it->second);
|
||||
}
|
||||
|
||||
void TextureReplacements::DumpVRAMWrite(u32 width, u32 height, const void* pixels)
|
||||
static constexpr std::array<u32, 4> s_texture_mode_shifts = {{2, 1, 0}};
|
||||
static constexpr std::array<u32, 4> s_texture_mode_revshifts = {{0, 1, 2}};
|
||||
static constexpr std::array<u32, 4> s_texture_mode_masks = {{3, 1, 0}};
|
||||
|
||||
void TextureReplacements::TransformTextureCoordinates(GPUTextureMode mode, u32 vram_x, u32 vram_y, u32 vram_width,
|
||||
u32 vram_height, u32* out_vram_width, u32* out_vram_height,
|
||||
u32* page_index, u32* page_offset_x, u32* page_offset_y,
|
||||
u32* page_width, u32* page_height)
|
||||
{
|
||||
std::string filename = GetVRAMWriteDumpFilename(width, height, pixels);
|
||||
if (filename.empty())
|
||||
const u32 shift = s_texture_mode_shifts[static_cast<u32>(mode)];
|
||||
const u32 revshift = s_texture_mode_revshifts[static_cast<u32>(mode)];
|
||||
const u32 page_x = vram_x / TEXTURE_REPLACEMENT_PAGE_VRAM_WIDTH;
|
||||
const u32 page_y = vram_y / TEXTURE_REPLACEMENT_PAGE_VRAM_HEIGHT;
|
||||
|
||||
*page_index = GetTextureReplacementPageIndex(page_x, page_y);
|
||||
|
||||
*page_offset_x = (vram_x % TEXTURE_REPLACEMENT_PAGE_VRAM_WIDTH) << shift;
|
||||
*page_offset_y = (vram_y % TEXTURE_REPLACEMENT_PAGE_VRAM_HEIGHT);
|
||||
|
||||
*page_width = std::min<u32>(vram_width << shift, (TEXTURE_REPLACEMENT_PAGE_WIDTH >> revshift) - *page_offset_x);
|
||||
*page_height = std::min<u32>(vram_height, TEXTURE_REPLACEMENT_PAGE_HEIGHT - *page_offset_y);
|
||||
|
||||
*out_vram_width = *page_width >> shift;
|
||||
*out_vram_height = *page_height;
|
||||
}
|
||||
|
||||
void TextureReplacements::UntransformTextureCoordinates(GPUTextureMode mode, u32 texture_width, u32 texture_height,
|
||||
u32 vram_x, u32 vram_y, u32* out_vram_width,
|
||||
u32* out_vram_height, u32* page_index, u32* page_offset_x,
|
||||
u32* page_offset_y, u32* page_width, u32* page_height)
|
||||
{
|
||||
const u32 shift = s_texture_mode_shifts[static_cast<u32>(mode)];
|
||||
const u32 revshift = s_texture_mode_revshifts[static_cast<u32>(mode)];
|
||||
const u32 page_x = vram_x / TEXTURE_REPLACEMENT_PAGE_VRAM_WIDTH;
|
||||
const u32 page_y = vram_y / TEXTURE_REPLACEMENT_PAGE_VRAM_HEIGHT;
|
||||
|
||||
*page_index = GetTextureReplacementPageIndex(page_x, page_y);
|
||||
|
||||
*page_offset_x = (vram_x % TEXTURE_REPLACEMENT_PAGE_VRAM_WIDTH) << shift;
|
||||
*page_offset_y = (vram_y % TEXTURE_REPLACEMENT_PAGE_VRAM_HEIGHT);
|
||||
|
||||
*page_width = std::min<u32>(texture_width, (TEXTURE_REPLACEMENT_PAGE_WIDTH >> revshift) - *page_offset_x);
|
||||
*page_height = std::min<u32>(texture_height, TEXTURE_REPLACEMENT_PAGE_HEIGHT - *page_offset_y);
|
||||
|
||||
*out_vram_width = (*page_width + s_texture_mode_masks[static_cast<u32>(mode)]) >> shift;
|
||||
*out_vram_height = *page_height;
|
||||
}
|
||||
|
||||
void TextureReplacements::InvalidateReplacementTexture(GPUTextureMode mode, u32 vram_x, u32 vram_y, u32 width,
|
||||
u32 height)
|
||||
{
|
||||
const u32 scale = g_settings.texture_replacements.replacement_texture_scale;
|
||||
|
||||
while (height > 0)
|
||||
{
|
||||
u32 current_x = vram_x;
|
||||
u32 remaining_width = width;
|
||||
u32 consume_height = height;
|
||||
|
||||
while (remaining_width > 0)
|
||||
{
|
||||
u32 consume_width, page_index, page_offset_x, page_offset_y, page_width, page_height;
|
||||
TransformTextureCoordinates(mode, current_x, vram_y, remaining_width, height, &consume_width, &consume_height,
|
||||
&page_index, &page_offset_x, &page_offset_y, &page_width, &page_height);
|
||||
|
||||
const u32 dummy_data_size = (page_width * scale) * (page_height * scale);
|
||||
if (m_texture_replacement_invalidate_buffer.size() < dummy_data_size)
|
||||
m_texture_replacement_invalidate_buffer.resize(dummy_data_size);
|
||||
|
||||
GetHWGPU()->UploadTextureReplacement(page_index, page_offset_x * scale, page_offset_y * scale, page_width * scale,
|
||||
page_height * scale, m_texture_replacement_invalidate_buffer.data(),
|
||||
page_width * scale * sizeof(u32));
|
||||
|
||||
current_x += consume_width;
|
||||
remaining_width -= consume_width;
|
||||
}
|
||||
|
||||
vram_y += consume_height;
|
||||
height -= consume_height;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureReplacements::UploadReplacementTexture(const TextureReplacementTexture* texture,
|
||||
const ReplacementEntry& entry, u32 vram_x, u32 vram_y)
|
||||
{
|
||||
const u32 scale = g_settings.texture_replacements.replacement_texture_scale;
|
||||
const u32 unscaled_width = (entry.width << s_texture_mode_shifts[static_cast<u32>(entry.mode)]);
|
||||
const u32 scaled_width = unscaled_width * scale;
|
||||
const u32 scaled_height = entry.height * scale;
|
||||
|
||||
if (texture->GetWidth() != scaled_width || texture->GetHeight() != scaled_height)
|
||||
{
|
||||
Log_VerbosePrintf("Resizing replacement texture from %ux%u to %ux%u", texture->GetWidth(), texture->GetHeight(),
|
||||
scaled_width, scaled_height);
|
||||
|
||||
TextureReplacementTexture resized_texture;
|
||||
resized_texture.ResizeFrom(texture, scaled_width, scaled_height);
|
||||
UploadReplacementTexture(&resized_texture, entry, vram_x, vram_y);
|
||||
return;
|
||||
|
||||
Common::RGBA8Image image;
|
||||
image.SetSize(width, height);
|
||||
|
||||
const u16* src_pixels = reinterpret_cast<const u16*>(pixels);
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
for (u32 x = 0; x < width; x++)
|
||||
{
|
||||
image.SetPixel(x, y, VRAMRGBA5551ToRGBA8888(*src_pixels));
|
||||
src_pixels++;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_settings.texture_replacements.dump_vram_write_force_alpha_channel)
|
||||
// this is the tricky part
|
||||
u32 current_y = vram_y + entry.offset_y;
|
||||
u32 remaining_height = entry.height;
|
||||
u32 texture_y = 0;
|
||||
|
||||
while (remaining_height > 0)
|
||||
{
|
||||
for (u32 y = 0; y < height; y++)
|
||||
u32 current_x = vram_x + entry.offset_x;
|
||||
u32 remaining_width = unscaled_width;
|
||||
u32 consume_height = remaining_height;
|
||||
|
||||
u32 texture_x = 0;
|
||||
|
||||
while (remaining_width > 0)
|
||||
{
|
||||
for (u32 x = 0; x < width; x++)
|
||||
image.SetPixel(x, y, image.GetPixel(x, y) | 0xFF000000u);
|
||||
u32 consume_width, page_index, page_offset_x, page_offset_y, page_width, page_height;
|
||||
UntransformTextureCoordinates(entry.mode, remaining_width, remaining_height, current_x, current_y, &consume_width,
|
||||
&consume_height, &page_index, &page_offset_x, &page_offset_y, &page_width,
|
||||
&page_height);
|
||||
|
||||
const u32 upload_width = std::min(page_width, remaining_width);
|
||||
const u32 upload_height = std::min(page_height, remaining_height);
|
||||
const u32 scaled_upload_width = upload_width * scale;
|
||||
const u32 scaled_upload_height = upload_height * scale;
|
||||
const u32 scaled_page_offset_x = page_offset_x * scale;
|
||||
const u32 scaled_page_offset_y = page_offset_y * scale;
|
||||
|
||||
Log_InfoPrintf("Uploading %ux%u to replacement page %u @ %u,%u", scaled_upload_width, scaled_upload_height,
|
||||
page_index, scaled_page_offset_x, scaled_page_offset_y);
|
||||
|
||||
GetHWGPU()->UploadTextureReplacement(page_index, scaled_page_offset_x, scaled_page_offset_y, scaled_upload_width,
|
||||
scaled_upload_height, texture->GetRowPixels(texture_y) + texture_x,
|
||||
texture->GetPitch());
|
||||
|
||||
texture_x += scaled_upload_width;
|
||||
remaining_width -= upload_width;
|
||||
current_x += consume_width;
|
||||
consume_height = upload_height;
|
||||
}
|
||||
|
||||
remaining_height -= consume_height;
|
||||
texture_y += consume_height;
|
||||
vram_y += consume_height;
|
||||
}
|
||||
}
|
||||
|
||||
void TextureReplacements::UploadReplacementTextures(u32 vram_x, u32 vram_y, u32 width, u32 height, const void* pixels)
|
||||
{
|
||||
const TextureReplacementHash hash = GetVRAMWriteHash(width, height, pixels);
|
||||
|
||||
const auto [lower, upper] = m_texture_replacements.equal_range(hash);
|
||||
if (lower == upper)
|
||||
{
|
||||
InvalidateReplacementTexture(GPUTextureMode::Palette4Bit, vram_x, vram_y, width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Dumping %ux%u VRAM write to '%s'", width, height, filename.c_str());
|
||||
if (!image.SaveToFile(filename.c_str()))
|
||||
Log_ErrorPrintf("Failed to dump %ux%u VRAM write to '%s'", width, height, filename.c_str());
|
||||
for (auto iter = lower; iter != upper; ++iter)
|
||||
{
|
||||
const ReplacementEntry& entry = iter->second;
|
||||
|
||||
const TextureReplacementTexture* texture = LoadTexture(entry.filename);
|
||||
if (!texture)
|
||||
{
|
||||
InvalidateReplacementTexture(entry.mode, vram_x + entry.offset_x, vram_y + entry.offset_y, entry.width,
|
||||
entry.height);
|
||||
continue;
|
||||
}
|
||||
|
||||
UploadReplacementTexture(texture, entry, vram_x, vram_y);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureReplacements::Shutdown()
|
||||
{
|
||||
m_texture_cache.clear();
|
||||
m_vram_write_replacements.clear();
|
||||
m_game_id.clear();
|
||||
m_texture_replacements.clear();
|
||||
decltype(m_texture_replacement_invalidate_buffer)().swap(m_texture_replacement_invalidate_buffer);
|
||||
}
|
||||
|
||||
std::string TextureReplacements::GetSourceDirectory() const
|
||||
{
|
||||
return Path::Combine(EmuFolders::Textures, m_game_id);
|
||||
const std::string& code = System::GetRunningCode();
|
||||
if (code.empty())
|
||||
return EmuFolders::Textures;
|
||||
else
|
||||
return Path::Combine(EmuFolders::Textures, code);
|
||||
}
|
||||
|
||||
std::string TextureReplacements::GetDumpDirectory() const
|
||||
{
|
||||
return Path::Combine(EmuFolders::Dumps, Path::Combine("textures", m_game_id));
|
||||
}
|
||||
|
||||
TextureReplacementHash TextureReplacements::GetVRAMWriteHash(u32 width, u32 height, const void* pixels) const
|
||||
TextureReplacementHash TextureReplacements::GetVRAMWriteHash(u32 width, u32 height, const void* pixels)
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits(pixels, width * height * sizeof(u16));
|
||||
return {hash.low64, hash.high64};
|
||||
}
|
||||
|
||||
std::string TextureReplacements::GetVRAMWriteDumpFilename(u32 width, u32 height, const void* pixels) const
|
||||
{
|
||||
if (m_game_id.empty())
|
||||
return {};
|
||||
|
||||
const TextureReplacementHash hash = GetVRAMWriteHash(width, height, pixels);
|
||||
const std::string dump_directory(GetDumpDirectory());
|
||||
std::string filename(Path::Combine(dump_directory, fmt::format("vram-write-{}.png", hash.ToString())));
|
||||
|
||||
if (FileSystem::FileExists(filename.c_str()))
|
||||
return {};
|
||||
|
||||
if (!FileSystem::EnsureDirectoryExists(dump_directory.c_str(), false))
|
||||
return {};
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
void TextureReplacements::Reload()
|
||||
{
|
||||
m_vram_write_replacements.clear();
|
||||
m_texture_replacements.clear();
|
||||
|
||||
if (!g_gpu || !g_gpu->IsHardwareRenderer())
|
||||
{
|
||||
m_texture_cache.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_settings.texture_replacements.AnyReplacementsEnabled())
|
||||
FindTextures(GetSourceDirectory());
|
||||
|
@ -163,6 +305,9 @@ void TextureReplacements::Reload()
|
|||
PreloadTextures();
|
||||
|
||||
PurgeUnreferencedTexturesFromCache();
|
||||
|
||||
if (g_settings.texture_replacements.enable_texture_replacements)
|
||||
ReuploadReplacementTextures();
|
||||
}
|
||||
|
||||
void TextureReplacements::PurgeUnreferencedTexturesFromCache()
|
||||
|
@ -177,13 +322,24 @@ void TextureReplacements::PurgeUnreferencedTexturesFromCache()
|
|||
old_map.erase(it2);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& it : m_texture_replacements)
|
||||
{
|
||||
auto it2 = old_map.find(it.second.filename);
|
||||
if (it2 != old_map.end())
|
||||
{
|
||||
m_texture_cache[it.second.filename] = std::move(it2->second);
|
||||
old_map.erase(it2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TextureReplacements::ParseReplacementFilename(const std::string& filename,
|
||||
TextureReplacementHash* replacement_hash,
|
||||
ReplacmentType* replacement_type)
|
||||
ReplacmentType* replacement_type, GPUTextureMode* out_mode,
|
||||
u16* out_offset_x, u16* out_offset_y, u16* out_width,
|
||||
u16* out_height)
|
||||
{
|
||||
const char* extension = std::strrchr(filename.c_str(), '.');
|
||||
const char* title = std::strrchr(filename.c_str(), '/');
|
||||
#ifdef _WIN32
|
||||
const char* title2 = std::strrchr(filename.c_str(), '\\');
|
||||
|
@ -191,24 +347,72 @@ bool TextureReplacements::ParseReplacementFilename(const std::string& filename,
|
|||
title = title2;
|
||||
#endif
|
||||
|
||||
if (!title || !extension)
|
||||
if (!title)
|
||||
return false;
|
||||
|
||||
title++;
|
||||
|
||||
const char* hashpart;
|
||||
const char* extension;
|
||||
|
||||
if (StringUtil::Strncasecmp(title, "vram-write-", 11) == 0)
|
||||
{
|
||||
hashpart = title + 11;
|
||||
if (std::strlen(hashpart) < 32)
|
||||
return false;
|
||||
|
||||
if (!replacement_hash->ParseString(std::string_view(hashpart, 32)))
|
||||
return false;
|
||||
|
||||
*replacement_type = ReplacmentType::VRAMWrite;
|
||||
*out_mode = GPUTextureMode::Direct16Bit;
|
||||
*out_offset_x = 0;
|
||||
*out_offset_y = 0;
|
||||
*out_width = 0;
|
||||
*out_height = 0;
|
||||
|
||||
extension = hashpart + 32;
|
||||
}
|
||||
else if (StringUtil::Strncasecmp(title, "texture-", 8) == 0)
|
||||
{
|
||||
// TODO: Make this much better...
|
||||
hashpart = title + 8;
|
||||
if (std::strlen(hashpart) < 42)
|
||||
return false;
|
||||
|
||||
if (!replacement_hash->ParseString(std::string_view(hashpart, 32)))
|
||||
return false;
|
||||
|
||||
const char* datapart = hashpart + 33;
|
||||
unsigned file_mode, file_offset_x, file_offset_y, file_width, file_height;
|
||||
if (std::sscanf(datapart, "%u-%u-%u-%u-%u", &file_mode, &file_offset_x, &file_offset_y, &file_width,
|
||||
&file_height) != 5)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_mode > static_cast<unsigned>(GPUTextureMode::Reserved_Direct16Bit) || file_offset_x >= VRAM_WIDTH ||
|
||||
(file_offset_x + file_width) > VRAM_WIDTH || file_offset_y >= VRAM_HEIGHT ||
|
||||
(file_offset_y + file_height) > VRAM_HEIGHT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*replacement_type = ReplacmentType::Texture;
|
||||
*out_mode = static_cast<GPUTextureMode>(file_mode);
|
||||
*out_offset_x = static_cast<u16>(file_offset_x);
|
||||
*out_offset_y = static_cast<u16>(file_offset_y);
|
||||
*out_width = static_cast<u16>(file_width);
|
||||
*out_height = static_cast<u16>(file_height);
|
||||
|
||||
extension = std::strchr(datapart, '.');
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!replacement_hash->ParseString(std::string_view(hashpart, static_cast<size_t>(extension - hashpart))))
|
||||
if (!extension || *extension == '\0')
|
||||
return false;
|
||||
|
||||
extension++;
|
||||
|
@ -228,8 +432,12 @@ bool TextureReplacements::ParseReplacementFilename(const std::string& filename,
|
|||
|
||||
void TextureReplacements::FindTextures(const std::string& dir)
|
||||
{
|
||||
const bool recursive = !System::GetRunningCode().empty();
|
||||
|
||||
FileSystem::FindResultsArray files;
|
||||
FileSystem::FindFiles(dir.c_str(), "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE, &files);
|
||||
FileSystem::FindFiles(dir.c_str(), "*",
|
||||
recursive ? (FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE) : (FILESYSTEM_FIND_FILES),
|
||||
&files);
|
||||
|
||||
for (FILESYSTEM_FIND_DATA& fd : files)
|
||||
{
|
||||
|
@ -238,13 +446,22 @@ void TextureReplacements::FindTextures(const std::string& dir)
|
|||
|
||||
TextureReplacementHash hash;
|
||||
ReplacmentType type;
|
||||
if (!ParseReplacementFilename(fd.FileName, &hash, &type))
|
||||
GPUTextureMode texture_mode;
|
||||
u16 texture_offset_x, texture_offset_y;
|
||||
u16 texture_width, texture_height;
|
||||
if (!ParseReplacementFilename(fd.FileName, &hash, &type, &texture_mode, &texture_offset_x, &texture_offset_y,
|
||||
&texture_width, &texture_height))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ReplacmentType::VRAMWrite:
|
||||
{
|
||||
if (!g_settings.texture_replacements.enable_vram_write_replacements)
|
||||
continue;
|
||||
|
||||
auto it = m_vram_write_replacements.find(hash);
|
||||
if (it != m_vram_write_replacements.end())
|
||||
{
|
||||
|
@ -255,10 +472,30 @@ void TextureReplacements::FindTextures(const std::string& dir)
|
|||
m_vram_write_replacements.emplace(hash, std::move(fd.FileName));
|
||||
}
|
||||
break;
|
||||
|
||||
case ReplacmentType::Texture:
|
||||
{
|
||||
if (!g_settings.texture_replacements.enable_texture_replacements)
|
||||
continue;
|
||||
|
||||
ReplacementEntry entry;
|
||||
entry.filename = std::move(fd.FileName);
|
||||
entry.mode = texture_mode;
|
||||
entry.offset_x = texture_offset_x;
|
||||
entry.offset_y = texture_offset_y;
|
||||
entry.width = texture_width;
|
||||
entry.height = texture_height;
|
||||
m_texture_replacements.emplace(hash, std::move(entry));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Found %zu replacement VRAM writes for '%s'", m_vram_write_replacements.size(), m_game_id.c_str());
|
||||
if (g_settings.texture_replacements.enable_vram_write_replacements)
|
||||
Log_InfoPrintf("Found %zu replacement VRAM writes", m_vram_write_replacements.size());
|
||||
|
||||
if (g_settings.texture_replacements.enable_texture_replacements)
|
||||
Log_InfoPrintf("Found %zu replacement textures", m_texture_replacements.size());
|
||||
}
|
||||
|
||||
const TextureReplacementTexture* TextureReplacements::LoadTexture(const std::string& filename)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include "common/hash_combine.h"
|
||||
#include "common/image.h"
|
||||
#include "gpu_types.h"
|
||||
#include "types.h"
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
@ -40,19 +41,49 @@ class TextureReplacements
|
|||
public:
|
||||
enum class ReplacmentType
|
||||
{
|
||||
VRAMWrite
|
||||
VRAMWrite,
|
||||
Texture
|
||||
};
|
||||
|
||||
enum : u32
|
||||
{
|
||||
TEXTURE_REPLACEMENT_PAGE_WIDTH = 256, // in page space
|
||||
TEXTURE_REPLACEMENT_PAGE_HEIGHT = 256, // in page space
|
||||
TEXTURE_REPLACEMENT_PAGE_VRAM_WIDTH = 64, // in vram space
|
||||
TEXTURE_REPLACEMENT_PAGE_VRAM_HEIGHT = 256, // in vram space
|
||||
TEXTURE_REPLACEMENT_X_PAGES = (VRAM_WIDTH / TEXTURE_REPLACEMENT_PAGE_VRAM_WIDTH),
|
||||
TEXTURE_REPLACEMENT_Y_PAGES = (VRAM_HEIGHT / TEXTURE_REPLACEMENT_PAGE_HEIGHT),
|
||||
TEXTURE_REPLACEMENT_PAGE_COUNT = (TEXTURE_REPLACEMENT_X_PAGES * TEXTURE_REPLACEMENT_Y_PAGES),
|
||||
};
|
||||
|
||||
TextureReplacements();
|
||||
~TextureReplacements();
|
||||
|
||||
const std::string GetGameID() const { return m_game_id; }
|
||||
void SetGameID(std::string game_id);
|
||||
static constexpr u32 GetTextureReplacementXPage(u32 vram_x) { return (vram_x * 4) / TEXTURE_REPLACEMENT_PAGE_WIDTH; }
|
||||
static constexpr u32 GetTextureReplacementXPageOffset(u32 vram_x)
|
||||
{
|
||||
return (vram_x * 4) % TEXTURE_REPLACEMENT_PAGE_WIDTH;
|
||||
}
|
||||
static constexpr u32 GetTextureReplacementYPage(u32 vram_y) { return vram_y / TEXTURE_REPLACEMENT_PAGE_HEIGHT; }
|
||||
static constexpr u32 GetTextureReplacementYPageOffset(u32 vram_y) { return vram_y % TEXTURE_REPLACEMENT_PAGE_HEIGHT; }
|
||||
static constexpr u32 GetTextureReplacementPageIndex(u32 page_x, u32 page_y)
|
||||
{
|
||||
return (page_y * TEXTURE_REPLACEMENT_X_PAGES) + page_x;
|
||||
}
|
||||
|
||||
u32 GetScaledReplacementTextureWidth();
|
||||
u32 GetScaledReplacementTextureHeight();
|
||||
void ReuploadReplacementTextures();
|
||||
|
||||
void OnSystemReset();
|
||||
|
||||
void Reload();
|
||||
|
||||
static TextureReplacementHash GetVRAMWriteHash(u32 width, u32 height, const void* pixels);
|
||||
|
||||
const TextureReplacementTexture* GetVRAMWriteReplacement(u32 width, u32 height, const void* pixels);
|
||||
void DumpVRAMWrite(u32 width, u32 height, const void* pixels);
|
||||
|
||||
void UploadReplacementTextures(u32 vram_x, u32 vram_y, u32 width, u32 height, const void* pixels);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
|
@ -62,29 +93,50 @@ private:
|
|||
size_t operator()(const TextureReplacementHash& hash);
|
||||
};
|
||||
|
||||
struct ReplacementEntry
|
||||
{
|
||||
std::string filename;
|
||||
u16 offset_x, offset_y;
|
||||
u16 width, height;
|
||||
GPUTextureMode mode;
|
||||
};
|
||||
|
||||
using VRAMWriteReplacementMap = std::unordered_map<TextureReplacementHash, std::string>;
|
||||
using TextureReplacementMap = std::unordered_multimap<TextureReplacementHash, ReplacementEntry>;
|
||||
using TextureCache = std::unordered_map<std::string, TextureReplacementTexture>;
|
||||
|
||||
static bool ParseReplacementFilename(const std::string& filename, TextureReplacementHash* replacement_hash,
|
||||
ReplacmentType* replacement_type);
|
||||
ReplacmentType* replacement_type, GPUTextureMode* out_mode, u16* out_offset_x,
|
||||
u16* out_offset_y, u16* out_width, u16* out_height);
|
||||
|
||||
std::string GetSourceDirectory() const;
|
||||
std::string GetDumpDirectory() const;
|
||||
|
||||
TextureReplacementHash GetVRAMWriteHash(u32 width, u32 height, const void* pixels) const;
|
||||
std::string GetVRAMWriteDumpFilename(u32 width, u32 height, const void* pixels) const;
|
||||
|
||||
void FindTextures(const std::string& dir);
|
||||
|
||||
const TextureReplacementTexture* LoadTexture(const std::string& filename);
|
||||
void PreloadTextures();
|
||||
void PurgeUnreferencedTexturesFromCache();
|
||||
|
||||
std::string m_game_id;
|
||||
void TransformTextureCoordinates(GPUTextureMode mode, u32 vram_x, u32 vram_y, u32 vram_width, u32 vram_height,
|
||||
u32* out_vram_width, u32* out_vram_height, u32* page_index, u32* page_offset_x,
|
||||
u32* page_offset_y, u32* page_width, u32* page_height);
|
||||
|
||||
void UntransformTextureCoordinates(GPUTextureMode mode, u32 texture_width, u32 texture_height, u32 vram_x, u32 vram_y,
|
||||
u32* out_vram_width, u32* out_vram_height, u32* page_index, u32* page_offset_x,
|
||||
u32* page_offset_y, u32* page_width, u32* page_height);
|
||||
|
||||
void InvalidateReplacementTextures();
|
||||
void InvalidateReplacementTexture(GPUTextureMode mode, u32 vram_x, u32 vram_y, u32 width, u32 height);
|
||||
void UploadReplacementTexture(const TextureReplacementTexture* texture, const ReplacementEntry& entry, u32 vram_x,
|
||||
u32 vram_y);
|
||||
|
||||
TextureCache m_texture_cache;
|
||||
|
||||
VRAMWriteReplacementMap m_vram_write_replacements;
|
||||
TextureReplacementMap m_texture_replacements;
|
||||
|
||||
std::vector<u32> m_texture_replacement_invalidate_buffer;
|
||||
};
|
||||
|
||||
extern TextureReplacements g_texture_replacements;
|
|
@ -3801,7 +3801,7 @@ void FullscreenUI::DrawAdvancedSettingsPage()
|
|||
|
||||
MenuHeading("Texture Dumping");
|
||||
|
||||
DrawToggleSetting(bsi, "Dump Replaceable VRAM Writes", "Writes textures which can be replaced to the dump directory.",
|
||||
DrawToggleSetting(bsi, "Dump Backgrounds", "Writes textures which can be replaced to the dump directory.",
|
||||
"TextureReplacements", "DumpVRAMWrites", false);
|
||||
DrawToggleSetting(bsi, "Set VRAM Write Dump Alpha Channel", "Clears the mask/transparency bit in VRAM write dumps.",
|
||||
"TextureReplacements", "DumpVRAMWriteForceAlphaChannel", true);
|
||||
|
|
Loading…
Reference in New Issue