GPUDevice: Support generating mipmaps

This commit is contained in:
Stenzek 2024-11-21 00:56:07 +10:00
parent e647192437
commit 3ff1b04576
No known key found for this signature in database
44 changed files with 898 additions and 527 deletions

View File

@ -31,7 +31,6 @@
X(GPUDevice) \ X(GPUDevice) \
X(GPUDump) \ X(GPUDump) \
X(GPUShaderCache) \ X(GPUShaderCache) \
X(GPUTexture) \
X(GPUTextureCache) \ X(GPUTextureCache) \
X(GPU_HW) \ X(GPU_HW) \
X(GPU_SW) \ X(GPU_SW) \

View File

@ -1470,7 +1470,8 @@ void GPU::WriteGP1(u32 value)
} }
break; break;
[[unlikely]] default : ERROR_LOG("Unimplemented GP1 command 0x{:02X}", command); [[unlikely]] default:
ERROR_LOG("Unimplemented GP1 command 0x{:02X}", command);
break; break;
} }
} }
@ -1518,7 +1519,8 @@ void GPU::HandleGetGPUInfoCommand(u32 value)
} }
break; break;
[[unlikely]] default : WARNING_LOG("Unhandled GetGPUInfo(0x{:02X})", subcommand); [[unlikely]] default:
WARNING_LOG("Unhandled GetGPUInfo(0x{:02X})", subcommand);
break; break;
} }
} }
@ -2213,7 +2215,7 @@ bool GPU::DeinterlaceExtractField(u32 dst_bufidx, GPUTexture* src, u32 x, u32 y,
m_deinterlace_buffers[dst_bufidx]->GetHeight() != height) m_deinterlace_buffers[dst_bufidx]->GetHeight() != height)
{ {
if (!g_gpu_device->ResizeTexture(&m_deinterlace_buffers[dst_bufidx], width, height, GPUTexture::Type::RenderTarget, if (!g_gpu_device->ResizeTexture(&m_deinterlace_buffers[dst_bufidx], width, height, GPUTexture::Type::RenderTarget,
GPUTexture::Format::RGBA8, false)) [[unlikely]] GPUTexture::Format::RGBA8, GPUTexture::Flags::None, false)) [[unlikely]]
{ {
return false; return false;
} }
@ -2258,7 +2260,7 @@ bool GPU::DeinterlaceSetTargetSize(u32 width, u32 height, bool preserve)
m_deinterlace_texture->GetHeight() != height) m_deinterlace_texture->GetHeight() != height)
{ {
if (!g_gpu_device->ResizeTexture(&m_deinterlace_texture, width, height, GPUTexture::Type::RenderTarget, if (!g_gpu_device->ResizeTexture(&m_deinterlace_texture, width, height, GPUTexture::Type::RenderTarget,
GPUTexture::Format::RGBA8, preserve)) [[unlikely]] GPUTexture::Format::RGBA8, GPUTexture::Flags::None, preserve)) [[unlikely]]
{ {
return false; return false;
} }
@ -2279,7 +2281,7 @@ bool GPU::ApplyChromaSmoothing()
m_chroma_smoothing_texture->GetHeight() != height) m_chroma_smoothing_texture->GetHeight() != height)
{ {
if (!g_gpu_device->ResizeTexture(&m_chroma_smoothing_texture, width, height, GPUTexture::Type::RenderTarget, if (!g_gpu_device->ResizeTexture(&m_chroma_smoothing_texture, width, height, GPUTexture::Type::RenderTarget,
GPUTexture::Format::RGBA8, false)) GPUTexture::Format::RGBA8, GPUTexture::Flags::None, false))
{ {
ClearDisplayTexture(); ClearDisplayTexture();
return false; return false;
@ -2540,8 +2542,8 @@ bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i displ
const GPUTexture::Format hdformat = const GPUTexture::Format hdformat =
g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetFormat() : GPUTexture::Format::RGBA8; g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetFormat() : GPUTexture::Format::RGBA8;
auto render_texture = auto render_texture = g_gpu_device->FetchAutoRecycleTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget,
g_gpu_device->FetchAutoRecycleTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, hdformat); hdformat, GPUTexture::Flags::None);
if (!render_texture) if (!render_texture)
return false; return false;

View File

@ -275,15 +275,9 @@ bool GPU_HW::Initialize(Error* error)
PrintSettingsToLog(); PrintSettingsToLog();
if (!CompileCommonShaders(error) || !CompilePipelines(error)) if (!CompileCommonShaders(error) || !CompilePipelines(error) || !CreateBuffers(error))
return false; return false;
if (!CreateBuffers())
{
Error::SetStringView(error, "Failed to create framebuffer");
return false;
}
if (m_use_texture_cache) if (m_use_texture_cache)
{ {
if (!GPUTextureCache::Initialize()) if (!GPUTextureCache::Initialize())
@ -366,7 +360,7 @@ bool GPU_HW::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
->FetchTexture( ->FetchTexture(
m_vram_texture->GetWidth(), m_vram_texture->GetHeight(), 1, 1, m_vram_texture->GetSamples(), m_vram_texture->GetWidth(), m_vram_texture->GetHeight(), 1, 1, m_vram_texture->GetSamples(),
m_vram_texture->IsMultisampled() ? GPUTexture::Type::RenderTarget : GPUTexture::Type::Texture, m_vram_texture->IsMultisampled() ? GPUTexture::Type::RenderTarget : GPUTexture::Type::Texture,
GPUTexture::Format::RGBA8, nullptr, 0) GPUTexture::Format::RGBA8, GPUTexture::Flags::None)
.release(); .release();
*host_texture = tex; *host_texture = tex;
if (!tex) if (!tex)
@ -538,8 +532,12 @@ void GPU_HW::UpdateSettings(const Settings& old_settings)
g_gpu_device->PurgeTexturePool(); g_gpu_device->PurgeTexturePool();
g_gpu_device->WaitForGPUIdle(); g_gpu_device->WaitForGPUIdle();
if (!CreateBuffers()) Error error;
if (!CreateBuffers(&error))
{
ERROR_LOG("Failed to recreate buffers: {}", error.GetDescription());
Panic("Failed to recreate buffers."); Panic("Failed to recreate buffers.");
}
UpdateDownsamplingLevels(); UpdateDownsamplingLevels();
RestoreDeviceContext(); RestoreDeviceContext();
@ -849,7 +847,7 @@ GPUTexture::Format GPU_HW::GetDepthBufferFormat() const
VRAM_DS_FORMAT; VRAM_DS_FORMAT;
} }
bool GPU_HW::CreateBuffers() bool GPU_HW::CreateBuffers(Error* error)
{ {
DestroyBuffers(); DestroyBuffers();
@ -859,28 +857,30 @@ bool GPU_HW::CreateBuffers()
const u8 samples = static_cast<u8>(m_multisamples); const u8 samples = static_cast<u8>(m_multisamples);
const bool needs_depth_buffer = m_write_mask_as_depth || m_pgxp_depth_buffer; const bool needs_depth_buffer = m_write_mask_as_depth || m_pgxp_depth_buffer;
// Needed for Metal resolve. const GPUTexture::Flags read_texture_flags =
const GPUTexture::Type read_texture_type = (g_gpu_device->GetRenderAPI() == RenderAPI::Metal && m_multisamples > 1) ? (m_multisamples > 1) ? GPUTexture::Flags::AllowMSAAResolveTarget : GPUTexture::Flags::None;
GPUTexture::Type::RWTexture : const GPUTexture::Flags vram_texture_flags =
GPUTexture::Type::Texture; m_use_rov_for_shader_blend ? GPUTexture::Flags::AllowBindAsImage : GPUTexture::Flags::None;
const GPUTexture::Type vram_texture_type =
m_use_rov_for_shader_blend ? GPUTexture::Type::RWTexture : GPUTexture::Type::RenderTarget;
const GPUTexture::Type depth_texture_type = const GPUTexture::Type depth_texture_type =
m_use_rov_for_shader_blend ? GPUTexture::Type::RWTexture : GPUTexture::Type::DepthStencil; m_use_rov_for_shader_blend ? GPUTexture::Type::Texture : GPUTexture::Type::DepthStencil;
if (!(m_vram_texture = g_gpu_device->FetchTexture(texture_width, texture_height, 1, 1, samples, vram_texture_type, if (!(m_vram_texture =
VRAM_RT_FORMAT)) || g_gpu_device->FetchTexture(texture_width, texture_height, 1, 1, samples, GPUTexture::Type::RenderTarget,
(needs_depth_buffer && VRAM_RT_FORMAT, vram_texture_flags, nullptr, 0, error)) ||
!(m_vram_depth_texture = g_gpu_device->FetchTexture(texture_width, texture_height, 1, 1, samples, (needs_depth_buffer && !(m_vram_depth_texture = g_gpu_device->FetchTexture(
depth_texture_type, GetDepthBufferFormat()))) || texture_width, texture_height, 1, 1, samples, depth_texture_type,
(m_pgxp_depth_buffer && !(m_vram_depth_copy_texture = GetDepthBufferFormat(), vram_texture_flags, nullptr, 0, error))) ||
g_gpu_device->FetchTexture(texture_width, texture_height, 1, 1, samples, (m_pgxp_depth_buffer && !(m_vram_depth_copy_texture = g_gpu_device->FetchTexture(
GPUTexture::Type::RenderTarget, VRAM_DS_COLOR_FORMAT))) || texture_width, texture_height, 1, 1, samples, GPUTexture::Type::RenderTarget,
VRAM_DS_COLOR_FORMAT, GPUTexture::Flags::None, nullptr, 0, error))) ||
!(m_vram_read_texture = !(m_vram_read_texture =
g_gpu_device->FetchTexture(texture_width, texture_height, 1, 1, 1, read_texture_type, VRAM_RT_FORMAT)) || g_gpu_device->FetchTexture(texture_width, texture_height, 1, 1, 1, GPUTexture::Type::Texture, VRAM_RT_FORMAT,
!(m_vram_readback_texture = g_gpu_device->FetchTexture(VRAM_WIDTH / 2, VRAM_HEIGHT, 1, 1, 1, read_texture_flags, nullptr, 0, error)) ||
GPUTexture::Type::RenderTarget, VRAM_RT_FORMAT))) !(m_vram_readback_texture =
g_gpu_device->FetchTexture(VRAM_WIDTH / 2, VRAM_HEIGHT, 1, 1, 1, GPUTexture::Type::RenderTarget,
VRAM_RT_FORMAT, GPUTexture::Flags::None, nullptr, 0, error)))
{ {
Error::AddPrefix(error, "Failed to create VRAM textures: ");
return false; return false;
} }
@ -895,26 +895,28 @@ bool GPU_HW::CreateBuffers()
DEV_LOG("Trying to import guest VRAM buffer for downloads..."); DEV_LOG("Trying to import guest VRAM buffer for downloads...");
m_vram_readback_download_texture = g_gpu_device->CreateDownloadTexture( m_vram_readback_download_texture = g_gpu_device->CreateDownloadTexture(
m_vram_readback_texture->GetWidth(), m_vram_readback_texture->GetHeight(), m_vram_readback_texture->GetFormat(), m_vram_readback_texture->GetWidth(), m_vram_readback_texture->GetHeight(), m_vram_readback_texture->GetFormat(),
g_vram, sizeof(g_vram), VRAM_WIDTH * sizeof(u16)); g_vram, sizeof(g_vram), VRAM_WIDTH * sizeof(u16), error);
if (!m_vram_readback_download_texture) if (!m_vram_readback_download_texture)
ERROR_LOG("Failed to create imported readback buffer"); ERROR_LOG("Failed to create imported readback buffer");
} }
if (!m_vram_readback_download_texture) if (!m_vram_readback_download_texture)
{ {
m_vram_readback_download_texture = g_gpu_device->CreateDownloadTexture( m_vram_readback_download_texture =
m_vram_readback_texture->GetWidth(), m_vram_readback_texture->GetHeight(), m_vram_readback_texture->GetFormat()); g_gpu_device->CreateDownloadTexture(m_vram_readback_texture->GetWidth(), m_vram_readback_texture->GetHeight(),
m_vram_readback_texture->GetFormat(), error);
if (!m_vram_readback_download_texture) if (!m_vram_readback_download_texture)
{ {
ERROR_LOG("Failed to create readback download texture"); Error::AddPrefix(error, "Failed to create readback download texture: ");
return false; return false;
} }
} }
if (g_gpu_device->GetFeatures().supports_texture_buffers) if (g_gpu_device->GetFeatures().supports_texture_buffers)
{ {
if (!(m_vram_upload_buffer = if (!(m_vram_upload_buffer = g_gpu_device->CreateTextureBuffer(GPUTextureBuffer::Format::R16UI,
g_gpu_device->CreateTextureBuffer(GPUTextureBuffer::Format::R16UI, GPUDevice::MIN_TEXEL_BUFFER_ELEMENTS))) GPUDevice::MIN_TEXEL_BUFFER_ELEMENTS, error)))
{ {
Error::AddPrefix(error, "Failed to create texture buffer: ");
return false; return false;
} }
@ -2930,9 +2932,9 @@ bool GPU_HW::BlitVRAMReplacementTexture(const GPUTextureCache::TextureReplacemen
{ {
g_gpu_device->RecycleTexture(std::move(m_vram_replacement_texture)); g_gpu_device->RecycleTexture(std::move(m_vram_replacement_texture));
if (!(m_vram_replacement_texture = if (!(m_vram_replacement_texture = g_gpu_device->FetchTexture(
g_gpu_device->FetchTexture(tex->GetWidth(), tex->GetHeight(), 1, 1, 1, GPUTexture::Type::DynamicTexture, tex->GetWidth(), tex->GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8,
GPUTexture::Format::RGBA8, tex->GetPixels(), tex->GetPitch()))) GPUTexture::Flags::None, tex->GetPixels(), tex->GetPitch())))
{ {
return false; return false;
} }
@ -3402,7 +3404,7 @@ void GPU_HW::UpdateVRAMOnGPU(u32 x, u32 y, u32 width, u32 height, const void* da
{ {
map_index = 0; map_index = 0;
upload_texture = g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture, upload_texture = g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture,
GPUTexture::Format::R16U, data, data_pitch); GPUTexture::Format::R16U, GPUTexture::Flags::None, data, data_pitch);
if (!upload_texture) if (!upload_texture)
{ {
ERROR_LOG("Failed to get {}x{} upload texture. Things are gonna break.", width, height); ERROR_LOG("Failed to get {}x{} upload texture. Things are gonna break.", width, height);
@ -3938,7 +3940,8 @@ void GPU_HW::UpdateDisplay()
m_vram_extract_texture->GetHeight() != read_height) m_vram_extract_texture->GetHeight() != read_height)
{ {
if (!g_gpu_device->ResizeTexture(&m_vram_extract_texture, scaled_display_width, read_height, if (!g_gpu_device->ResizeTexture(&m_vram_extract_texture, scaled_display_width, read_height,
GPUTexture::Type::RenderTarget, GPUTexture::Format::RGBA8)) [[unlikely]] GPUTexture::Type::RenderTarget, GPUTexture::Format::RGBA8,
GPUTexture::Flags::None)) [[unlikely]]
{ {
ClearDisplayTexture(); ClearDisplayTexture();
return; return;
@ -3952,7 +3955,7 @@ void GPU_HW::UpdateDisplay()
((m_vram_extract_depth_texture && m_vram_extract_depth_texture->GetWidth() == scaled_display_width && ((m_vram_extract_depth_texture && m_vram_extract_depth_texture->GetWidth() == scaled_display_width &&
m_vram_extract_depth_texture->GetHeight() == scaled_display_height) || m_vram_extract_depth_texture->GetHeight() == scaled_display_height) ||
!g_gpu_device->ResizeTexture(&m_vram_extract_depth_texture, scaled_display_width, scaled_display_height, !g_gpu_device->ResizeTexture(&m_vram_extract_depth_texture, scaled_display_width, scaled_display_height,
GPUTexture::Type::RenderTarget, VRAM_DS_COLOR_FORMAT))) GPUTexture::Type::RenderTarget, VRAM_DS_COLOR_FORMAT, GPUTexture::Flags::None)))
{ {
depth_source->MakeReadyForSampling(); depth_source->MakeReadyForSampling();
g_gpu_device->InvalidateRenderTarget(m_vram_extract_depth_texture.get()); g_gpu_device->InvalidateRenderTarget(m_vram_extract_depth_texture.get());
@ -4090,15 +4093,16 @@ void GPU_HW::DownsampleFramebufferAdaptive(GPUTexture* source, u32 left, u32 top
if (!m_downsample_texture || m_downsample_texture->GetWidth() != width || m_downsample_texture->GetHeight() != height) if (!m_downsample_texture || m_downsample_texture->GetWidth() != width || m_downsample_texture->GetHeight() != height)
{ {
g_gpu_device->RecycleTexture(std::move(m_downsample_texture)); g_gpu_device->RecycleTexture(std::move(m_downsample_texture));
m_downsample_texture = m_downsample_texture = g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget,
g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::RenderTarget, VRAM_RT_FORMAT); VRAM_RT_FORMAT, GPUTexture::Flags::None);
} }
std::unique_ptr<GPUTexture, GPUDevice::PooledTextureDeleter> level_texture = g_gpu_device->FetchAutoRecycleTexture( std::unique_ptr<GPUTexture, GPUDevice::PooledTextureDeleter> level_texture =
width, height, 1, m_downsample_scale_or_levels, 1, GPUTexture::Type::Texture, VRAM_RT_FORMAT); g_gpu_device->FetchAutoRecycleTexture(width, height, 1, m_downsample_scale_or_levels, 1, GPUTexture::Type::Texture,
std::unique_ptr<GPUTexture, GPUDevice::PooledTextureDeleter> weight_texture = VRAM_RT_FORMAT, GPUTexture::Flags::None);
g_gpu_device->FetchAutoRecycleTexture(std::max(width >> (m_downsample_scale_or_levels - 1), 1u), std::unique_ptr<GPUTexture, GPUDevice::PooledTextureDeleter> weight_texture = g_gpu_device->FetchAutoRecycleTexture(
std::max(height >> (m_downsample_scale_or_levels - 1), 1u), 1, 1, 1, std::max(width >> (m_downsample_scale_or_levels - 1), 1u),
GPUTexture::Type::RenderTarget, GPUTexture::Format::R8); std::max(height >> (m_downsample_scale_or_levels - 1), 1u), 1, 1, 1, GPUTexture::Type::RenderTarget,
GPUTexture::Format::R8, GPUTexture::Flags::None);
if (!m_downsample_texture || !level_texture || !weight_texture) if (!m_downsample_texture || !level_texture || !weight_texture)
{ {
ERROR_LOG("Failed to create {}x{} RTs for adaptive downsampling", width, height); ERROR_LOG("Failed to create {}x{} RTs for adaptive downsampling", width, height);
@ -4205,8 +4209,8 @@ void GPU_HW::DownsampleFramebufferBoxFilter(GPUTexture* source, u32 left, u32 to
m_downsample_texture->GetHeight() != ds_height) m_downsample_texture->GetHeight() != ds_height)
{ {
g_gpu_device->RecycleTexture(std::move(m_downsample_texture)); g_gpu_device->RecycleTexture(std::move(m_downsample_texture));
m_downsample_texture = m_downsample_texture = g_gpu_device->FetchTexture(ds_width, ds_height, 1, 1, 1, GPUTexture::Type::RenderTarget,
g_gpu_device->FetchTexture(ds_width, ds_height, 1, 1, 1, GPUTexture::Type::RenderTarget, VRAM_RT_FORMAT); VRAM_RT_FORMAT, GPUTexture::Flags::None);
} }
if (!m_downsample_texture) if (!m_downsample_texture)
{ {

View File

@ -155,7 +155,7 @@ private:
/// Returns true if a depth buffer should be created. /// Returns true if a depth buffer should be created.
GPUTexture::Format GetDepthBufferFormat() const; GPUTexture::Format GetDepthBufferFormat() const;
bool CreateBuffers(); bool CreateBuffers(Error* error);
void ClearFramebuffer(); void ClearFramebuffer();
void DestroyBuffers(); void DestroyBuffers();

View File

@ -2048,8 +2048,9 @@ GPUTextureCache::HashCacheEntry* GPUTextureCache::LookupHashCache(SourceKey key,
entry.ref_count = 0; entry.ref_count = 0;
entry.last_used_frame = 0; entry.last_used_frame = 0;
entry.sources = {}; entry.sources = {};
entry.texture = g_gpu_device->FetchTexture(TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT, 1, 1, 1, entry.texture =
GPUTexture::Type::Texture, GPUTexture::Format::RGBA8); g_gpu_device->FetchTexture(TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT, 1, 1, 1, GPUTexture::Type::Texture,
GPUTexture::Format::RGBA8, GPUTexture::Flags::None);
if (!entry.texture) if (!entry.texture)
{ {
ERROR_LOG("Failed to create texture."); ERROR_LOG("Failed to create texture.");
@ -3285,8 +3286,9 @@ void GPUTextureCache::ApplyTextureReplacements(SourceKey key, HashType tex_hash,
{ {
// NOTE: Not recycled, it's unlikely to be reused. // NOTE: Not recycled, it's unlikely to be reused.
s_state.replacement_texture_render_target.reset(); s_state.replacement_texture_render_target.reset();
if (!(s_state.replacement_texture_render_target = g_gpu_device->CreateTexture( if (!(s_state.replacement_texture_render_target =
new_width, new_height, 1, 1, 1, GPUTexture::Type::RenderTarget, REPLACEMENT_TEXTURE_FORMAT))) g_gpu_device->CreateTexture(new_width, new_height, 1, 1, 1, GPUTexture::Type::RenderTarget,
REPLACEMENT_TEXTURE_FORMAT, GPUTexture::Flags::None)))
{ {
ERROR_LOG("Failed to create {}x{} render target.", new_width, new_height); ERROR_LOG("Failed to create {}x{} render target.", new_width, new_height);
return; return;
@ -3294,8 +3296,8 @@ void GPUTextureCache::ApplyTextureReplacements(SourceKey key, HashType tex_hash,
} }
// Grab the actual texture beforehand, in case we OOM. // Grab the actual texture beforehand, in case we OOM.
std::unique_ptr<GPUTexture> replacement_tex = std::unique_ptr<GPUTexture> replacement_tex = g_gpu_device->FetchTexture(
g_gpu_device->FetchTexture(new_width, new_height, 1, 1, 1, GPUTexture::Type::Texture, REPLACEMENT_TEXTURE_FORMAT); new_width, new_height, 1, 1, 1, GPUTexture::Type::Texture, REPLACEMENT_TEXTURE_FORMAT, GPUTexture::Flags::None);
if (!replacement_tex) if (!replacement_tex)
{ {
ERROR_LOG("Failed to create {}x{} texture.", new_width, new_height); ERROR_LOG("Failed to create {}x{} texture.", new_width, new_height);
@ -3319,7 +3321,7 @@ void GPUTextureCache::ApplyTextureReplacements(SourceKey key, HashType tex_hash,
{ {
const auto temp_texture = g_gpu_device->FetchAutoRecycleTexture( const auto temp_texture = g_gpu_device->FetchAutoRecycleTexture(
si.image.GetWidth(), si.image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, REPLACEMENT_TEXTURE_FORMAT, si.image.GetWidth(), si.image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, REPLACEMENT_TEXTURE_FORMAT,
si.image.GetPixels(), si.image.GetPitch()); GPUTexture::Flags::None, si.image.GetPixels(), si.image.GetPitch());
if (!temp_texture) if (!temp_texture)
continue; continue;

View File

@ -99,8 +99,8 @@ GPUTexture* GPU_SW::GetDisplayTexture(u32 width, u32 height, GPUTexture::Format
{ {
ClearDisplayTexture(); ClearDisplayTexture();
g_gpu_device->RecycleTexture(std::move(m_upload_texture)); g_gpu_device->RecycleTexture(std::move(m_upload_texture));
m_upload_texture = m_upload_texture = g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture, format,
g_gpu_device->FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::DynamicTexture, format, nullptr, 0); GPUTexture::Flags::AllowMap, nullptr, 0);
if (!m_upload_texture) [[unlikely]] if (!m_upload_texture) [[unlikely]]
ERROR_LOG("Failed to create {}x{} {} texture", width, height, static_cast<u32>(format)); ERROR_LOG("Failed to create {}x{} {} texture", width, height, static_cast<u32>(format));
} }

View File

@ -1049,9 +1049,9 @@ void SaveStateSelectorUI::InitializeListEntry(ListEntry* li, ExtendedSaveStateIn
if (ssi->screenshot.IsValid()) if (ssi->screenshot.IsValid())
{ {
li->preview_texture = g_gpu_device->FetchTexture(ssi->screenshot.GetWidth(), ssi->screenshot.GetHeight(), 1, 1, 1, li->preview_texture = g_gpu_device->FetchTexture(
GPUTexture::Type::Texture, GPUTexture::Format::RGBA8, ssi->screenshot.GetWidth(), ssi->screenshot.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
ssi->screenshot.GetPixels(), ssi->screenshot.GetPitch()); GPUTexture::Format::RGBA8, GPUTexture::Flags::None, ssi->screenshot.GetPixels(), ssi->screenshot.GetPitch());
if (!li->preview_texture) [[unlikely]] if (!li->preview_texture) [[unlikely]]
ERROR_LOG("Failed to upload save state image to GPU"); ERROR_LOG("Failed to upload save state image to GPU");
} }

View File

@ -139,11 +139,8 @@ bool D3D11Device::CreateDeviceAndMainSwapChain(std::string_view adapter, Feature
return false; return false;
} }
if (!CreateBuffers()) if (!CreateBuffers(error))
{
Error::SetStringView(error, "Failed to create buffers");
return false; return false;
}
return true; return true;
} }
@ -514,11 +511,11 @@ void D3D11Device::WaitForGPUIdle()
TrimTexturePool(); TrimTexturePool();
} }
bool D3D11Device::CreateBuffers() bool D3D11Device::CreateBuffers(Error* error)
{ {
if (!m_vertex_buffer.Create(D3D11_BIND_VERTEX_BUFFER, VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE) || if (!m_vertex_buffer.Create(D3D11_BIND_VERTEX_BUFFER, VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE, error) ||
!m_index_buffer.Create(D3D11_BIND_INDEX_BUFFER, INDEX_BUFFER_SIZE, INDEX_BUFFER_SIZE) || !m_index_buffer.Create(D3D11_BIND_INDEX_BUFFER, INDEX_BUFFER_SIZE, INDEX_BUFFER_SIZE, error) ||
!m_uniform_buffer.Create(D3D11_BIND_CONSTANT_BUFFER, MIN_UNIFORM_BUFFER_SIZE, MAX_UNIFORM_BUFFER_SIZE)) !m_uniform_buffer.Create(D3D11_BIND_CONSTANT_BUFFER, MIN_UNIFORM_BUFFER_SIZE, MAX_UNIFORM_BUFFER_SIZE, error))
{ {
ERROR_LOG("Failed to create vertex/index/uniform buffers."); ERROR_LOG("Failed to create vertex/index/uniform buffers.");
return false; return false;
@ -612,7 +609,7 @@ void D3D11Device::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u3
bool D3D11Device::IsRenderTargetBound(const D3D11Texture* tex) const bool D3D11Device::IsRenderTargetBound(const D3D11Texture* tex) const
{ {
if (tex->IsRenderTarget() || tex->IsRWTexture()) if (tex->IsRenderTarget() || tex->HasFlag(GPUTexture::Flags::AllowBindAsImage))
{ {
for (u32 i = 0; i < m_num_current_render_targets; i++) for (u32 i = 0; i < m_num_current_render_targets; i++)
{ {
@ -1053,7 +1050,7 @@ void D3D11Device::SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* s
// Runtime will null these if we don't... // Runtime will null these if we don't...
DebugAssert(!texture || DebugAssert(!texture ||
!((texture->IsRenderTarget() || texture->IsRWTexture()) && !((texture->IsRenderTarget() || texture->HasFlag(GPUTexture::Flags::AllowBindAsImage)) &&
IsRenderTargetBound(static_cast<D3D11Texture*>(texture))) || IsRenderTargetBound(static_cast<D3D11Texture*>(texture))) ||
!(texture->IsDepthStencil() && !(texture->IsDepthStencil() &&
(!m_current_depth_target || m_current_depth_target != static_cast<D3D11Texture*>(texture)))); (!m_current_depth_target || m_current_depth_target != static_cast<D3D11Texture*>(texture))));
@ -1100,7 +1097,7 @@ void D3D11Device::UnbindTexture(D3D11Texture* tex)
} }
} }
if (tex->IsRenderTarget() || tex->IsRWTexture()) if (tex->IsRenderTarget() || tex->HasFlag(GPUTexture::Flags::AllowBindAsImage))
{ {
for (u32 i = 0; i < m_num_current_render_targets; i++) for (u32 i = 0; i < m_num_current_render_targets; i++)
{ {

View File

@ -50,15 +50,18 @@ public:
std::optional<bool> exclusive_fullscreen_control, std::optional<bool> exclusive_fullscreen_control,
Error* error) override; Error* error) override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags,
const void* data = nullptr, u32 data_stride = 0) override; const void* data = nullptr, u32 data_stride = 0,
std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) override; Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements) override; std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config, Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements,
Error* error = nullptr) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, Error* error = nullptr) override;
u32 memory_stride) override; std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, u32 memory_stride,
Error* error = nullptr) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override; bool SupportsTextureFormat(GPUTexture::Format format) const override;
void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
@ -141,7 +144,7 @@ private:
void SetFeatures(FeatureMask disabled_features); void SetFeatures(FeatureMask disabled_features);
bool CreateBuffers(); bool CreateBuffers(Error* error);
void DestroyBuffers(); void DestroyBuffers();
void BindUniformBuffer(u32 offset, u32 size); void BindUniformBuffer(u32 offset, u32 size);
void UnbindComputePipeline(); void UnbindComputePipeline();

View File

@ -8,6 +8,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/error.h" #include "common/error.h"
#include "common/log.h" #include "common/log.h"
#include "common/small_string.h"
LOG_CHANNEL(GPUDevice); LOG_CHANNEL(GPUDevice);
@ -27,7 +28,7 @@ D3D11StreamBuffer::~D3D11StreamBuffer()
Destroy(); Destroy();
} }
bool D3D11StreamBuffer::Create(D3D11_BIND_FLAG bind_flags, u32 min_size, u32 max_size) bool D3D11StreamBuffer::Create(D3D11_BIND_FLAG bind_flags, u32 min_size, u32 max_size, Error* error)
{ {
D3D11_FEATURE_DATA_D3D11_OPTIONS options = {}; D3D11_FEATURE_DATA_D3D11_OPTIONS options = {};
HRESULT hr = D3D11Device::GetD3DDevice()->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options)); HRESULT hr = D3D11Device::GetD3DDevice()->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options));
@ -72,7 +73,7 @@ bool D3D11StreamBuffer::Create(D3D11_BIND_FLAG bind_flags, u32 min_size, u32 max
hr = D3D11Device::GetD3DDevice()->CreateBuffer(&desc, nullptr, &buffer); hr = D3D11Device::GetD3DDevice()->CreateBuffer(&desc, nullptr, &buffer);
if (FAILED(hr)) [[unlikely]] if (FAILED(hr)) [[unlikely]]
{ {
ERROR_LOG("Creating buffer failed: {}", Error::CreateHResult(hr).GetDescription()); Error::SetHResult(error, TinyString::from_format("CreateBuffer({}) failed: ", create_size), hr);
return false; return false;
} }

View File

@ -9,6 +9,8 @@
#include <d3d11_1.h> #include <d3d11_1.h>
#include <wrl/client.h> #include <wrl/client.h>
class Error;
class D3D11StreamBuffer class D3D11StreamBuffer
{ {
public: public:
@ -26,7 +28,7 @@ public:
ALWAYS_INLINE bool IsMapped() const { return m_mapped; } ALWAYS_INLINE bool IsMapped() const { return m_mapped; }
ALWAYS_INLINE bool IsUsingMapNoOverwrite() const { return m_use_map_no_overwrite; } ALWAYS_INLINE bool IsUsingMapNoOverwrite() const { return m_use_map_no_overwrite; }
bool Create(D3D11_BIND_FLAG bind_flags, u32 min_size, u32 max_size); bool Create(D3D11_BIND_FLAG bind_flags, u32 min_size, u32 max_size, Error* error);
void Destroy(); void Destroy();
struct MappingResult struct MappingResult

View File

@ -6,6 +6,7 @@
#include "d3d_common.h" #include "d3d_common.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/error.h"
#include "common/log.h" #include "common/log.h"
#include "common/string_util.h" #include "common/string_util.h"
@ -17,9 +18,11 @@ LOG_CHANNEL(GPUDevice);
std::unique_ptr<GPUTexture> D3D11Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> D3D11Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data, u32 data_stride) GPUTexture::Flags flags, const void* data /* = nullptr */,
u32 data_stride /* = 0 */, Error* error /* = nullptr */)
{ {
return D3D11Texture::Create(m_device.Get(), width, height, layers, levels, samples, type, format, data, data_stride); return D3D11Texture::Create(m_device.Get(), width, height, layers, levels, samples, type, format, flags, data,
data_stride, error);
} }
bool D3D11Device::SupportsTextureFormat(GPUTexture::Format format) const bool D3D11Device::SupportsTextureFormat(GPUTexture::Format format) const
@ -44,7 +47,7 @@ void D3D11Sampler::SetDebugName(std::string_view name)
SetD3DDebugObjectName(m_ss.Get(), name); SetD3DDebugObjectName(m_ss.Get(), name);
} }
std::unique_ptr<GPUSampler> D3D11Device::CreateSampler(const GPUSampler::Config& config) std::unique_ptr<GPUSampler> D3D11Device::CreateSampler(const GPUSampler::Config& config, Error* error)
{ {
static constexpr std::array<D3D11_TEXTURE_ADDRESS_MODE, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{ static constexpr std::array<D3D11_TEXTURE_ADDRESS_MODE, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{
D3D11_TEXTURE_ADDRESS_WRAP, // Repeat D3D11_TEXTURE_ADDRESS_WRAP, // Repeat
@ -87,7 +90,7 @@ std::unique_ptr<GPUSampler> D3D11Device::CreateSampler(const GPUSampler::Config&
const HRESULT hr = m_device->CreateSamplerState(&desc, ss.GetAddressOf()); const HRESULT hr = m_device->CreateSamplerState(&desc, ss.GetAddressOf());
if (FAILED(hr)) [[unlikely]] if (FAILED(hr)) [[unlikely]]
{ {
ERROR_LOG("CreateSamplerState() failed: {:08X}", static_cast<unsigned>(hr)); Error::SetHResult(error, "CreateSamplerState() failed: ", hr);
return {}; return {};
} }
@ -95,10 +98,10 @@ std::unique_ptr<GPUSampler> D3D11Device::CreateSampler(const GPUSampler::Config&
} }
D3D11Texture::D3D11Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, D3D11Texture::D3D11Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> srv, Flags flags, ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> srv,
ComPtr<ID3D11View> rtv_dsv, ComPtr<ID3D11UnorderedAccessView> uav) ComPtr<ID3D11View> rtv_dsv, ComPtr<ID3D11UnorderedAccessView> uav)
: GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels), : GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
static_cast<u8>(samples), type, format), static_cast<u8>(samples), type, format, flags),
m_texture(std::move(texture)), m_srv(std::move(srv)), m_rtv_dsv(std::move(rtv_dsv)), m_uav(std::move(uav)) m_texture(std::move(texture)), m_srv(std::move(srv)), m_rtv_dsv(std::move(rtv_dsv)), m_uav(std::move(uav))
{ {
} }
@ -127,7 +130,7 @@ void D3D11Texture::CommitClear(ID3D11DeviceContext1* context)
else else
context->ClearDepthStencilView(GetD3DDSV(), D3D11_CLEAR_DEPTH, GetClearDepth(), 0); context->ClearDepthStencilView(GetD3DDSV(), D3D11_CLEAR_DEPTH, GetClearDepth(), 0);
} }
else if (IsRenderTarget() || IsRWTexture()) else if (IsRenderTarget())
{ {
if (m_state == GPUTexture::State::Invalidated) if (m_state == GPUTexture::State::Invalidated)
context->DiscardView(GetD3DRTV()); context->DiscardView(GetD3DRTV());
@ -141,7 +144,7 @@ void D3D11Texture::CommitClear(ID3D11DeviceContext1* context)
bool D3D11Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/, bool D3D11Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/,
u32 level /*= 0*/) u32 level /*= 0*/)
{ {
if (m_type == Type::DynamicTexture) if (HasFlag(Flags::AllowMap))
{ {
void* map; void* map;
u32 map_stride; u32 map_stride;
@ -171,7 +174,7 @@ bool D3D11Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data,
bool D3D11Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/, bool D3D11Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer /*= 0*/,
u32 level /*= 0*/) u32 level /*= 0*/)
{ {
if (m_type != Type::DynamicTexture || (x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) || if (!HasFlag(Flags::AllowMap) || (x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) ||
layer > m_layers || level > m_levels) layer > m_layers || level > m_levels)
{ {
return false; return false;
@ -207,6 +210,12 @@ void D3D11Texture::Unmap()
m_mapped_subresource = 0; m_mapped_subresource = 0;
} }
void D3D11Texture::GenerateMipmaps()
{
DebugAssert(HasFlag(Flags::AllowGenerateMipmaps));
D3D11Device::GetD3DContext()->GenerateMips(m_srv.Get());
}
void D3D11Texture::SetDebugName(std::string_view name) void D3D11Texture::SetDebugName(std::string_view name)
{ {
SetD3DDebugObjectName(m_texture.Get(), name); SetD3DDebugObjectName(m_texture.Get(), name);
@ -218,43 +227,57 @@ DXGI_FORMAT D3D11Texture::GetDXGIFormat() const
} }
std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels, std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels,
u32 samples, Type type, Format format, u32 samples, Type type, Format format, Flags flags,
const void* initial_data /* = nullptr */, const void* initial_data, u32 initial_data_stride, Error* error)
u32 initial_data_stride /* = 0 */)
{ {
if (!ValidateConfig(width, height, layers, layers, samples, type, format)) if (!ValidateConfig(width, height, layers, levels, samples, type, format, flags, error))
return nullptr; return nullptr;
u32 bind_flags = 0; u32 bind_flags = 0;
D3D11_USAGE usage = D3D11_USAGE_DEFAULT; D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
u32 cpu_access = 0; u32 cpu_access = 0;
u32 misc = 0;
switch (type) switch (type)
{ {
case Type::RenderTarget:
bind_flags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
break;
case Type::DepthStencil:
bind_flags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
break;
case Type::Texture: case Type::Texture:
bind_flags = D3D11_BIND_SHADER_RESOURCE; bind_flags = D3D11_BIND_SHADER_RESOURCE;
break; break;
case Type::DynamicTexture:
bind_flags = D3D11_BIND_SHADER_RESOURCE; case Type::RenderTarget:
usage = D3D11_USAGE_DYNAMIC; bind_flags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
cpu_access = D3D11_CPU_ACCESS_WRITE;
break; break;
case Type::RWTexture:
bind_flags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; case Type::DepthStencil:
break; bind_flags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
default:
break; break;
DefaultCaseIsUnreachable();
}
if ((flags & Flags::AllowBindAsImage) != Flags::None)
{
DebugAssert(levels == 1);
bind_flags |= D3D11_BIND_UNORDERED_ACCESS;
}
if ((flags & Flags::AllowGenerateMipmaps) != Flags::None)
{
// Needs RT annoyingly.
bind_flags |= D3D11_BIND_RENDER_TARGET;
misc = D3D11_RESOURCE_MISC_GENERATE_MIPS;
}
if ((flags & Flags::AllowMap) != Flags::None)
{
DebugAssert(type == Type::Texture);
usage = D3D11_USAGE_DYNAMIC;
cpu_access = D3D11_CPU_ACCESS_WRITE;
} }
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(format); const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(format);
CD3D11_TEXTURE2D_DESC desc(fm.resource_format, width, height, layers, levels, bind_flags, usage, cpu_access, samples, CD3D11_TEXTURE2D_DESC desc(fm.resource_format, width, height, layers, levels, bind_flags, usage, cpu_access, samples,
0, 0); 0, misc);
D3D11_SUBRESOURCE_DATA srd; D3D11_SUBRESOURCE_DATA srd;
srd.pSysMem = initial_data; srd.pSysMem = initial_data;
@ -265,9 +288,7 @@ std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 wid
const HRESULT tex_hr = device->CreateTexture2D(&desc, initial_data ? &srd : nullptr, texture.GetAddressOf()); const HRESULT tex_hr = device->CreateTexture2D(&desc, initial_data ? &srd : nullptr, texture.GetAddressOf());
if (FAILED(tex_hr)) if (FAILED(tex_hr))
{ {
ERROR_LOG("Create texture failed: 0x{:08X} ({}x{} levels:{} samples:{} format:{} bind_flags:{:X} initial_data:{})", Error::SetHResult(error, "CreateTexture2D() failed: ", tex_hr);
static_cast<unsigned>(tex_hr), width, height, levels, samples, static_cast<unsigned>(format), bind_flags,
initial_data);
return nullptr; return nullptr;
} }
@ -288,7 +309,7 @@ std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 wid
const HRESULT hr = device->CreateShaderResourceView(texture.Get(), &srv_desc, srv.GetAddressOf()); const HRESULT hr = device->CreateShaderResourceView(texture.Get(), &srv_desc, srv.GetAddressOf());
if (FAILED(hr)) [[unlikely]] if (FAILED(hr)) [[unlikely]]
{ {
ERROR_LOG("Create SRV for texture failed: 0x{:08X}", static_cast<unsigned>(hr)); Error::SetHResult(error, "CreateShaderResourceView() failed: ", hr);
return nullptr; return nullptr;
} }
} }
@ -303,7 +324,7 @@ std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 wid
const HRESULT hr = device->CreateRenderTargetView(texture.Get(), &rtv_desc, rtv.GetAddressOf()); const HRESULT hr = device->CreateRenderTargetView(texture.Get(), &rtv_desc, rtv.GetAddressOf());
if (FAILED(hr)) [[unlikely]] if (FAILED(hr)) [[unlikely]]
{ {
ERROR_LOG("Create RTV for texture failed: 0x{:08X}", static_cast<unsigned>(hr)); Error::SetHResult(error, "CreateRenderTargetView() failed: ", hr);
return nullptr; return nullptr;
} }
@ -318,7 +339,7 @@ std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 wid
const HRESULT hr = device->CreateDepthStencilView(texture.Get(), &dsv_desc, dsv.GetAddressOf()); const HRESULT hr = device->CreateDepthStencilView(texture.Get(), &dsv_desc, dsv.GetAddressOf());
if (FAILED(hr)) [[unlikely]] if (FAILED(hr)) [[unlikely]]
{ {
ERROR_LOG("Create DSV for texture failed: 0x{:08X}", static_cast<unsigned>(hr)); Error::SetHResult(error, "CreateDepthStencilView() failed: ", hr);
return nullptr; return nullptr;
} }
@ -334,12 +355,12 @@ std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 wid
const HRESULT hr = device->CreateUnorderedAccessView(texture.Get(), &uav_desc, uav.GetAddressOf()); const HRESULT hr = device->CreateUnorderedAccessView(texture.Get(), &uav_desc, uav.GetAddressOf());
if (FAILED(hr)) [[unlikely]] if (FAILED(hr)) [[unlikely]]
{ {
ERROR_LOG("Create UAV for texture failed: 0x{:08X}", static_cast<unsigned>(hr)); Error::SetHResult(error, "CreateUnorderedAccessView() failed: ", hr);
return nullptr; return nullptr;
} }
} }
return std::unique_ptr<D3D11Texture>(new D3D11Texture(width, height, layers, levels, samples, type, format, return std::unique_ptr<D3D11Texture>(new D3D11Texture(width, height, layers, levels, samples, type, format, flags,
std::move(texture), std::move(srv), std::move(rtv_dsv), std::move(texture), std::move(srv), std::move(rtv_dsv),
std::move(uav))); std::move(uav)));
} }
@ -350,10 +371,10 @@ D3D11TextureBuffer::D3D11TextureBuffer(Format format, u32 size_in_elements) : GP
D3D11TextureBuffer::~D3D11TextureBuffer() = default; D3D11TextureBuffer::~D3D11TextureBuffer() = default;
bool D3D11TextureBuffer::CreateBuffer() bool D3D11TextureBuffer::CreateBuffer(Error* error)
{ {
const u32 size_in_bytes = GetSizeInBytes(); const u32 size_in_bytes = GetSizeInBytes();
if (!m_buffer.Create(D3D11_BIND_SHADER_RESOURCE, size_in_bytes, size_in_bytes)) if (!m_buffer.Create(D3D11_BIND_SHADER_RESOURCE, size_in_bytes, size_in_bytes, error))
return false; return false;
static constexpr std::array<DXGI_FORMAT, static_cast<u32>(Format::MaxCount)> dxgi_formats = {{ static constexpr std::array<DXGI_FORMAT, static_cast<u32>(Format::MaxCount)> dxgi_formats = {{
@ -366,7 +387,7 @@ bool D3D11TextureBuffer::CreateBuffer()
D3D11Device::GetD3DDevice()->CreateShaderResourceView(m_buffer.GetD3DBuffer(), &srv_desc, m_srv.GetAddressOf()); D3D11Device::GetD3DDevice()->CreateShaderResourceView(m_buffer.GetD3DBuffer(), &srv_desc, m_srv.GetAddressOf());
if (FAILED(hr)) [[unlikely]] if (FAILED(hr)) [[unlikely]]
{ {
ERROR_LOG("CreateShaderResourceView() failed: {:08X}", static_cast<unsigned>(hr)); Error::SetHResult(error, "CreateShaderResourceView() failed: ", hr);
return false; return false;
} }
@ -395,10 +416,10 @@ void D3D11TextureBuffer::SetDebugName(std::string_view name)
} }
std::unique_ptr<GPUTextureBuffer> D3D11Device::CreateTextureBuffer(GPUTextureBuffer::Format format, std::unique_ptr<GPUTextureBuffer> D3D11Device::CreateTextureBuffer(GPUTextureBuffer::Format format,
u32 size_in_elements) u32 size_in_elements, Error* error /* = nullptr */)
{ {
std::unique_ptr<D3D11TextureBuffer> tb = std::make_unique<D3D11TextureBuffer>(format, size_in_elements); std::unique_ptr<D3D11TextureBuffer> tb = std::make_unique<D3D11TextureBuffer>(format, size_in_elements);
if (!tb->CreateBuffer()) if (!tb->CreateBuffer(error))
tb.reset(); tb.reset();
return tb; return tb;
@ -416,7 +437,8 @@ D3D11DownloadTexture::~D3D11DownloadTexture()
D3D11DownloadTexture::Unmap(); D3D11DownloadTexture::Unmap();
} }
std::unique_ptr<D3D11DownloadTexture> D3D11DownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format) std::unique_ptr<D3D11DownloadTexture> D3D11DownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format,
Error* error)
{ {
D3D11_TEXTURE2D_DESC desc = {}; D3D11_TEXTURE2D_DESC desc = {};
desc.Width = width; desc.Width = width;
@ -433,7 +455,7 @@ std::unique_ptr<D3D11DownloadTexture> D3D11DownloadTexture::Create(u32 width, u3
HRESULT hr = D3D11Device::GetD3DDevice()->CreateTexture2D(&desc, nullptr, tex.GetAddressOf()); HRESULT hr = D3D11Device::GetD3DDevice()->CreateTexture2D(&desc, nullptr, tex.GetAddressOf());
if (FAILED(hr)) if (FAILED(hr))
{ {
ERROR_LOG("CreateTexture2D() failed: {:08X}", hr); Error::SetHResult(error, "CreateTexture2D() failed: ", hr);
return {}; return {};
} }
@ -520,15 +542,16 @@ void D3D11DownloadTexture::SetDebugName(std::string_view name)
SetD3DDebugObjectName(m_texture.Get(), name); SetD3DDebugObjectName(m_texture.Get(), name);
} }
std::unique_ptr<GPUDownloadTexture> D3D11Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) std::unique_ptr<GPUDownloadTexture> D3D11Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
Error* error /* = nullptr */)
{ {
return D3D11DownloadTexture::Create(width, height, format); return D3D11DownloadTexture::Create(width, height, format, error);
} }
std::unique_ptr<GPUDownloadTexture> D3D11Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<GPUDownloadTexture> D3D11Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, void* memory, size_t memory_size,
u32 memory_stride) u32 memory_stride, Error* error /* = nullptr */)
{ {
ERROR_LOG("D3D11 cannot import memory for download textures"); Error::SetStringView(error, "D3D11 cannot import memory for download textures");
return {}; return {};
} }

View File

@ -77,8 +77,8 @@ public:
ALWAYS_INLINE operator bool() const { return static_cast<bool>(m_texture); } ALWAYS_INLINE operator bool() const { return static_cast<bool>(m_texture); }
static std::unique_ptr<D3D11Texture> Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels, static std::unique_ptr<D3D11Texture> Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels,
u32 samples, Type type, Format format, const void* initial_data = nullptr, u32 samples, Type type, Format format, Flags flags,
u32 initial_data_stride = 0); const void* initial_data, u32 initial_data_stride, Error* error);
D3D11_TEXTURE2D_DESC GetDesc() const; D3D11_TEXTURE2D_DESC GetDesc() const;
void CommitClear(ID3D11DeviceContext1* context); void CommitClear(ID3D11DeviceContext1* context);
@ -86,11 +86,12 @@ public:
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
void Unmap() override; void Unmap() override;
void GenerateMipmaps() override;
void SetDebugName(std::string_view name) override; void SetDebugName(std::string_view name) override;
private: private:
D3D11Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, D3D11Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, Flags flags,
ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> srv, ComPtr<ID3D11View> rtv_dsv, ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> srv, ComPtr<ID3D11View> rtv_dsv,
ComPtr<ID3D11UnorderedAccessView> uav); ComPtr<ID3D11UnorderedAccessView> uav);
@ -111,7 +112,7 @@ public:
ALWAYS_INLINE ID3D11ShaderResourceView* GetSRV() const { return m_srv.Get(); } ALWAYS_INLINE ID3D11ShaderResourceView* GetSRV() const { return m_srv.Get(); }
ALWAYS_INLINE ID3D11ShaderResourceView* const* GetSRVArray() const { return m_srv.GetAddressOf(); } ALWAYS_INLINE ID3D11ShaderResourceView* const* GetSRVArray() const { return m_srv.GetAddressOf(); }
bool CreateBuffer(); bool CreateBuffer(Error* error);
// Inherited via GPUTextureBuffer // Inherited via GPUTextureBuffer
void* Map(u32 required_elements) override; void* Map(u32 required_elements) override;
@ -129,7 +130,7 @@ class D3D11DownloadTexture final : public GPUDownloadTexture
public: public:
~D3D11DownloadTexture() override; ~D3D11DownloadTexture() override;
static std::unique_ptr<D3D11DownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format); static std::unique_ptr<D3D11DownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format, Error* error);
void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height, void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height,
u32 src_layer, u32 src_level, bool use_transfer_pitch) override; u32 src_layer, u32 src_level, bool use_transfer_pitch) override;

View File

@ -118,11 +118,6 @@ void D3D12::GraphicsPipelineBuilder::SetMultisamples(u32 multisamples)
m_desc.SampleDesc.Count = multisamples; m_desc.SampleDesc.Count = multisamples;
} }
void D3D12::GraphicsPipelineBuilder::SetNoCullRasterizationState()
{
SetRasterizationState(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, false);
}
void D3D12::GraphicsPipelineBuilder::SetDepthState(bool depth_test, bool depth_write, D3D12_COMPARISON_FUNC compare_op) void D3D12::GraphicsPipelineBuilder::SetDepthState(bool depth_test, bool depth_write, D3D12_COMPARISON_FUNC compare_op)
{ {
m_desc.DepthStencilState.DepthEnable = depth_test; m_desc.DepthStencilState.DepthEnable = depth_test;
@ -141,11 +136,6 @@ void D3D12::GraphicsPipelineBuilder::SetStencilState(bool stencil_test, u8 read_
m_desc.DepthStencilState.BackFace = back; m_desc.DepthStencilState.BackFace = back;
} }
void D3D12::GraphicsPipelineBuilder::SetNoDepthTestState()
{
SetDepthState(false, false, D3D12_COMPARISON_FUNC_ALWAYS);
}
void D3D12::GraphicsPipelineBuilder::SetNoStencilState() void D3D12::GraphicsPipelineBuilder::SetNoStencilState()
{ {
D3D12_DEPTH_STENCILOP_DESC empty = {}; D3D12_DEPTH_STENCILOP_DESC empty = {};
@ -170,18 +160,6 @@ void D3D12::GraphicsPipelineBuilder::SetBlendState(u32 rt, bool blend_enable, D3
m_desc.BlendState.IndependentBlendEnable = TRUE; m_desc.BlendState.IndependentBlendEnable = TRUE;
} }
void D3D12::GraphicsPipelineBuilder::SetColorWriteMask(u32 rt, u8 write_mask /* = D3D12_COLOR_WRITE_ENABLE_ALL */)
{
m_desc.BlendState.RenderTarget[rt].RenderTargetWriteMask = write_mask;
}
void D3D12::GraphicsPipelineBuilder::SetNoBlendingState()
{
SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, D3D12_BLEND_ZERO,
D3D12_BLEND_OP_ADD, D3D12_COLOR_WRITE_ENABLE_ALL);
m_desc.BlendState.IndependentBlendEnable = FALSE;
}
void D3D12::GraphicsPipelineBuilder::ClearRenderTargets() void D3D12::GraphicsPipelineBuilder::ClearRenderTargets()
{ {
m_desc.NumRenderTargets = 0; m_desc.NumRenderTargets = 0;

View File

@ -80,21 +80,14 @@ public:
void SetMultisamples(u32 multisamples); void SetMultisamples(u32 multisamples);
void SetNoCullRasterizationState();
void SetDepthState(bool depth_test, bool depth_write, D3D12_COMPARISON_FUNC compare_op); void SetDepthState(bool depth_test, bool depth_write, D3D12_COMPARISON_FUNC compare_op);
void SetStencilState(bool stencil_test, u8 read_mask, u8 write_mask, const D3D12_DEPTH_STENCILOP_DESC& front, void SetStencilState(bool stencil_test, u8 read_mask, u8 write_mask, const D3D12_DEPTH_STENCILOP_DESC& front,
const D3D12_DEPTH_STENCILOP_DESC& back); const D3D12_DEPTH_STENCILOP_DESC& back);
void SetNoDepthTestState();
void SetNoStencilState(); void SetNoStencilState();
void SetBlendState(u32 rt, bool blend_enable, D3D12_BLEND src_factor, D3D12_BLEND dst_factor, D3D12_BLEND_OP op, void SetBlendState(u32 rt, bool blend_enable, D3D12_BLEND src_factor, D3D12_BLEND dst_factor, D3D12_BLEND_OP op,
D3D12_BLEND alpha_src_factor, D3D12_BLEND alpha_dst_factor, D3D12_BLEND_OP alpha_op, D3D12_BLEND alpha_src_factor, D3D12_BLEND alpha_dst_factor, D3D12_BLEND_OP alpha_op,
u8 write_mask = D3D12_COLOR_WRITE_ENABLE_ALL); u8 write_mask = D3D12_COLOR_WRITE_ENABLE_ALL);
void SetColorWriteMask(u32 rt, u8 write_mask = D3D12_COLOR_WRITE_ENABLE_ALL);
void SetNoBlendingState();
void ClearRenderTargets(); void ClearRenderTargets();

View File

@ -83,7 +83,6 @@ bool D3D12DescriptorHeapManager::Allocate(D3D12DescriptorHandle* handle)
return true; return true;
} }
Panic("Out of fixed descriptors");
return false; return false;
} }

View File

@ -46,7 +46,6 @@ enum : u32
FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024, FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024,
TEXTURE_BUFFER_SIZE = 64 * 1024 * 1024, TEXTURE_BUFFER_SIZE = 64 * 1024 * 1024,
// UNIFORM_PUSH_CONSTANTS_STAGES = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
UNIFORM_PUSH_CONSTANTS_SIZE = 128, UNIFORM_PUSH_CONSTANTS_SIZE = 128,
MAX_UNIFORM_BUFFER_SIZE = 1024, MAX_UNIFORM_BUFFER_SIZE = 1024,
@ -65,6 +64,55 @@ static DynamicHeapArray<u8> s_pipeline_cache_data;
static u32 s_debug_scope_depth = 0; static u32 s_debug_scope_depth = 0;
#endif #endif
static constexpr const u32 s_mipmap_blit_vs[] = {
0x43425844, 0xe0f571cf, 0x51234ef3, 0x3a6beab4, 0x141cd2ef, 0x00000001, 0x000003ac, 0x00000005, 0x00000034,
0x00000144, 0x00000178, 0x000001d0, 0x00000310, 0x46454452, 0x00000108, 0x00000001, 0x00000068, 0x00000001,
0x0000003c, 0xfffe0500, 0x00008100, 0x000000e0, 0x31314452, 0x0000003c, 0x00000018, 0x00000020, 0x00000028,
0x00000024, 0x0000000c, 0x00000000, 0x0000005c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000001, 0x00000001, 0x424f4255, 0x6b636f6c, 0xababab00, 0x0000005c, 0x00000001, 0x00000080, 0x00000010,
0x00000000, 0x00000000, 0x000000a8, 0x00000000, 0x00000010, 0x00000002, 0x000000bc, 0x00000000, 0xffffffff,
0x00000000, 0xffffffff, 0x00000000, 0x72735f75, 0x65725f63, 0x66007463, 0x74616f6c, 0xabab0034, 0x00030001,
0x00040001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000b3, 0x7263694d,
0x666f736f, 0x52282074, 0x4c482029, 0x53204c53, 0x65646168, 0x6f432072, 0x6c69706d, 0x31207265, 0x00312e30,
0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000006, 0x00000001, 0x00000000,
0x00000101, 0x565f5653, 0x65747265, 0x00444978, 0x4e47534f, 0x00000050, 0x00000002, 0x00000008, 0x00000038,
0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000c03, 0x00000041, 0x00000000, 0x00000001, 0x00000003,
0x00000001, 0x0000000f, 0x43584554, 0x44524f4f, 0x5f565300, 0x69736f50, 0x6e6f6974, 0xababab00, 0x58454853,
0x00000138, 0x00010050, 0x0000004e, 0x0100086a, 0x04000059, 0x00208e46, 0x00000000, 0x00000001, 0x04000060,
0x00101012, 0x00000000, 0x00000006, 0x03000065, 0x00102032, 0x00000000, 0x04000067, 0x001020f2, 0x00000001,
0x00000001, 0x02000068, 0x00000001, 0x0b00008c, 0x00100012, 0x00000000, 0x00004001, 0x00000001, 0x00004001,
0x00000001, 0x0010100a, 0x00000000, 0x00004001, 0x00000000, 0x07000001, 0x00100042, 0x00000000, 0x0010100a,
0x00000000, 0x00004001, 0x00000002, 0x05000056, 0x00100032, 0x00000000, 0x00100086, 0x00000000, 0x0b000032,
0x00102032, 0x00000000, 0x00100046, 0x00000000, 0x00208ae6, 0x00000000, 0x00000000, 0x00208046, 0x00000000,
0x00000000, 0x0f000032, 0x00102032, 0x00000001, 0x00100046, 0x00000000, 0x00004002, 0x40000000, 0xc0000000,
0x00000000, 0x00000000, 0x00004002, 0xbf800000, 0x3f800000, 0x00000000, 0x00000000, 0x08000036, 0x001020c2,
0x00000001, 0x00004002, 0x00000000, 0x00000000, 0x00000000, 0x3f800000, 0x0100003e, 0x54415453, 0x00000094,
0x00000007, 0x00000001, 0x00000000, 0x00000003, 0x00000002, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000};
static constexpr const u32 s_mipmap_blit_ps[] = {
0x43425844, 0x25500f77, 0x71f24271, 0x5f83f8b8, 0x3f405943, 0x00000001, 0x0000026c, 0x00000005, 0x00000034,
0x000000f0, 0x00000124, 0x00000158, 0x000001d0, 0x46454452, 0x000000b4, 0x00000000, 0x00000000, 0x00000002,
0x0000003c, 0xffff0500, 0x00008100, 0x0000008b, 0x31314452, 0x0000003c, 0x00000018, 0x00000020, 0x00000028,
0x00000024, 0x0000000c, 0x00000000, 0x0000007c, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000001, 0x00000001, 0x00000085, 0x00000002, 0x00000005, 0x00000004, 0xffffffff, 0x00000000, 0x00000001,
0x0000000d, 0x706d6173, 0x73735f30, 0x6d617300, 0x4d003070, 0x6f726369, 0x74666f73, 0x29522820, 0x534c4820,
0x6853204c, 0x72656461, 0x6d6f4320, 0x656c6970, 0x30312072, 0xab00312e, 0x4e475349, 0x0000002c, 0x00000001,
0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000303, 0x43584554, 0x44524f4f,
0xababab00, 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
0x00000000, 0x0000000f, 0x545f5653, 0x65677261, 0xabab0074, 0x58454853, 0x00000070, 0x00000050, 0x0000001c,
0x0100086a, 0x0300005a, 0x00106000, 0x00000000, 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x03001062,
0x00101032, 0x00000000, 0x03000065, 0x001020f2, 0x00000000, 0x8b000045, 0x800000c2, 0x00155543, 0x001020f2,
0x00000000, 0x00101046, 0x00000000, 0x00107e46, 0x00000000, 0x00106000, 0x00000000, 0x0100003e, 0x54415453,
0x00000094, 0x00000002, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000};
D3D12Device::D3D12Device() D3D12Device::D3D12Device()
{ {
m_render_api = RenderAPI::D3D12; m_render_api = RenderAPI::D3D12;
@ -523,7 +571,9 @@ bool D3D12Device::CreateDescriptorHeaps(Error* error)
m_device->CreateUnorderedAccessView(nullptr, nullptr, &null_uav_desc, m_null_uav_descriptor.cpu_handle); m_device->CreateUnorderedAccessView(nullptr, nullptr, &null_uav_desc, m_null_uav_descriptor.cpu_handle);
// Same for samplers. // Same for samplers.
m_point_sampler = GetSampler(GPUSampler::GetNearestConfig()); m_point_sampler = GetSampler(GPUSampler::GetNearestConfig(), error);
if (!m_point_sampler) [[unlikely]]
return false;
for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++) for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++)
m_current_samplers[i] = m_point_sampler; m_current_samplers[i] = m_point_sampler;
return true; return true;
@ -2100,7 +2150,7 @@ void D3D12Device::UnbindTexture(D3D12Texture* tex)
} }
} }
if (tex->IsRenderTarget() || tex->IsRWTexture()) if (tex->IsRenderTarget() || tex->HasFlag(GPUTexture::Flags::AllowBindAsImage))
{ {
for (u32 i = 0; i < m_num_current_render_targets; i++) for (u32 i = 0; i < m_num_current_render_targets; i++)
{ {
@ -2134,6 +2184,137 @@ void D3D12Device::UnbindTextureBuffer(D3D12TextureBuffer* buf)
m_dirty_flags |= DIRTY_FLAG_TEXTURES; m_dirty_flags |= DIRTY_FLAG_TEXTURES;
} }
void D3D12Device::RenderTextureMipmap(D3D12Texture* texture, u32 dst_level, u32 dst_width, u32 dst_height,
u32 src_level, u32 src_width, u32 src_height)
{
ID3D12RootSignature* rootsig =
m_root_signatures[0][static_cast<size_t>(GPUPipeline::Layout::SingleTextureAndPushConstants)].Get();
ComPtr<ID3D12PipelineState>& pipeline = m_mipmap_render_pipelines[static_cast<size_t>(texture->GetFormat())];
if (!pipeline)
{
D3D12::GraphicsPipelineBuilder gpb;
gpb.SetRootSignature(rootsig);
gpb.SetPrimitiveTopologyType(D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE);
gpb.SetRenderTarget(0, texture->GetDXGIFormat());
gpb.SetVertexShader(s_mipmap_blit_vs, std::size(s_mipmap_blit_vs));
gpb.SetPixelShader(s_mipmap_blit_ps, std::size(s_mipmap_blit_ps));
gpb.SetRasterizationState(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, false);
gpb.SetDepthState(false, false, D3D12_COMPARISON_FUNC_ALWAYS);
gpb.SetBlendState(0, false, D3D12_BLEND_ZERO, D3D12_BLEND_ONE, D3D12_BLEND_OP_ADD, D3D12_BLEND_ZERO,
D3D12_BLEND_ONE, D3D12_BLEND_OP_ADD, D3D12_COLOR_WRITE_ENABLE_ALL);
const std::wstring name = StringUtil::UTF8StringToWideString(
TinyString::from_format("MipmapRender-{}", GPUTexture::GetFormatName(texture->GetFormat())));
Error error;
if (m_pipeline_library)
{
HRESULT hr =
m_pipeline_library->LoadGraphicsPipeline(name.c_str(), gpb.GetDesc(), IID_PPV_ARGS(pipeline.GetAddressOf()));
if (FAILED(hr))
{
// E_INVALIDARG = not found.
if (hr != E_INVALIDARG)
ERROR_LOG("LoadGraphicsPipeline() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
// Need to create it normally.
pipeline = gpb.Create(m_device.Get(), &error, false);
// Store if it wasn't an OOM or something else.
if (pipeline && hr == E_INVALIDARG)
{
hr = m_pipeline_library->StorePipeline(name.c_str(), pipeline.Get());
if (FAILED(hr))
ERROR_LOG("StorePipeline() failed with HRESULT {:08X}", static_cast<unsigned>(hr));
}
}
}
else
{
pipeline = gpb.Create(m_device.Get(), &error, false);
}
if (!pipeline)
{
ERROR_LOG("Failed to compile mipmap render pipeline for {}: {}", GPUTexture::GetFormatName(texture->GetFormat()),
error.GetDescription());
return;
}
}
EndRenderPass();
// we need a temporary SRV and RTV for each mip level
// Safe to use the init buffer after exec, because everything will be done with the texture.
D3D12DescriptorHandle rtv_handle;
while (!GetRTVHeapManager().Allocate(&rtv_handle))
SubmitCommandList(false, "Allocate RTV for RenderTextureMipmap()");
D3D12DescriptorHandle srv_handle;
while (!GetDescriptorHeapManager().Allocate(&srv_handle))
SubmitCommandList(false, "Allocate SRV for RenderTextureMipmap()");
// Setup views. This will be a partial view for the SRV.
D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {texture->GetDXGIFormat(), D3D12_RTV_DIMENSION_TEXTURE2D};
rtv_desc.Texture2D = {dst_level, 0u};
m_device->CreateRenderTargetView(texture->GetResource(), &rtv_desc, rtv_handle);
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {texture->GetDXGIFormat(), D3D12_SRV_DIMENSION_TEXTURE2D,
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
srv_desc.Texture2D = {src_level, 1u, 0u, 0.0f};
m_device->CreateShaderResourceView(texture->GetResource(), &srv_desc, srv_handle);
// *now* we don't have to worry about running out of anything.
ID3D12GraphicsCommandList4* cmdlist = GetCommandList();
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
{
texture->TransitionSubresourceToState(cmdlist, src_level, texture->GetResourceState(),
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_RENDER_TARGET)
{
texture->TransitionSubresourceToState(cmdlist, dst_level, texture->GetResourceState(),
D3D12_RESOURCE_STATE_RENDER_TARGET);
}
const D3D12_RENDER_PASS_RENDER_TARGET_DESC rt_desc = {.cpuDescriptor = rtv_handle,
.BeginningAccess =
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD,
.EndingAccess = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE};
cmdlist->BeginRenderPass(1, &rt_desc, nullptr, D3D12_RENDER_PASS_FLAG_NONE);
const D3D12_VIEWPORT vp = {0.0f, 0.0f, static_cast<float>(dst_width), static_cast<float>(dst_height), 0.0f, 1.0f};
cmdlist->RSSetViewports(1, &vp);
const D3D12_RECT scissor = {0, 0, static_cast<LONG>(dst_width), static_cast<LONG>(dst_height)};
cmdlist->RSSetScissorRects(1, &scissor);
cmdlist->SetPipelineState(pipeline.Get());
cmdlist->SetGraphicsRootDescriptorTable(0, srv_handle);
cmdlist->SetGraphicsRootDescriptorTable(1, static_cast<D3D12Sampler*>(m_linear_sampler.get())->GetDescriptor());
cmdlist->DrawInstanced(3, 1, 0, 0);
cmdlist->EndRenderPass();
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
{
texture->TransitionSubresourceToState(cmdlist, src_level, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
texture->GetResourceState());
}
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_RENDER_TARGET)
{
texture->TransitionSubresourceToState(cmdlist, dst_level, D3D12_RESOURCE_STATE_RENDER_TARGET,
texture->GetResourceState());
}
// Must destroy after current cmdlist.
DeferDescriptorDestruction(m_descriptor_heap_manager, &srv_handle);
DeferDescriptorDestruction(m_rtv_heap_manager, &rtv_handle);
// Restore for next normal draw.
SetViewport(GetCommandList());
SetScissor(GetCommandList());
m_dirty_flags |= LAYOUT_DEPENDENT_DIRTY_STATE;
}
void D3D12Device::SetViewport(const GSVector4i rc) void D3D12Device::SetViewport(const GSVector4i rc)
{ {
if (m_current_viewport.eq(rc)) if (m_current_viewport.eq(rc))

View File

@ -71,15 +71,18 @@ public:
std::optional<bool> exclusive_fullscreen_control, std::optional<bool> exclusive_fullscreen_control,
Error* error) override; Error* error) override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags,
const void* data = nullptr, u32 data_stride = 0) override; const void* data = nullptr, u32 data_stride = 0,
std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) override; Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements) override; std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config, Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements,
Error* error = nullptr) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, Error* error = nullptr) override;
u32 memory_stride) override; std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, u32 memory_stride,
Error* error = nullptr) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override; bool SupportsTextureFormat(GPUTexture::Format format) const override;
void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
@ -191,6 +194,9 @@ public:
void UnbindTexture(D3D12Texture* tex); void UnbindTexture(D3D12Texture* tex);
void UnbindTextureBuffer(D3D12TextureBuffer* buf); void UnbindTextureBuffer(D3D12TextureBuffer* buf);
void RenderTextureMipmap(D3D12Texture* texture, u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level,
u32 src_width, u32 src_height);
protected: protected:
bool CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features, const WindowInfo& wi, bool CreateDeviceAndMainSwapChain(std::string_view adapter, FeatureMask disabled_features, const WindowInfo& wi,
GPUVSyncMode vsync_mode, bool allow_present_throttle, GPUVSyncMode vsync_mode, bool allow_present_throttle,
@ -253,7 +259,7 @@ private:
void DestroyDescriptorHeaps(); void DestroyDescriptorHeaps();
bool CreateTimestampQuery(); bool CreateTimestampQuery();
void DestroyTimestampQuery(); void DestroyTimestampQuery();
D3D12DescriptorHandle GetSampler(const GPUSampler::Config& config); D3D12DescriptorHandle GetSampler(const GPUSampler::Config& config, Error* error);
void DestroySamplers(); void DestroySamplers();
void DestroyDeferredObjects(u64 fence_value); void DestroyDeferredObjects(u64 fence_value);
@ -261,10 +267,13 @@ private:
void MoveToNextCommandList(); void MoveToNextCommandList();
bool CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format, bool CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format,
D3D12DescriptorHandle* dh); D3D12DescriptorHandle* dh, Error* error);
bool CreateRTVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh); bool CreateRTVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh,
bool CreateDSVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh); Error* error);
bool CreateUAVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh); bool CreateDSVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh,
Error* error);
bool CreateUAVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh,
Error* error);
bool IsRenderTargetBound(const GPUTexture* tex) const; bool IsRenderTargetBound(const GPUTexture* tex) const;
@ -354,6 +363,9 @@ private:
GSVector4i m_current_scissor = {}; GSVector4i m_current_scissor = {};
D3D12SwapChain* m_current_swap_chain = nullptr; D3D12SwapChain* m_current_swap_chain = nullptr;
std::array<ComPtr<ID3D12PipelineState>, static_cast<size_t>(GPUTexture::Format::MaxCount)> m_mipmap_render_pipelines =
{};
}; };
class D3D12SwapChain : public GPUSwapChain class D3D12SwapChain : public GPUSwapChain

View File

@ -18,12 +18,12 @@
LOG_CHANNEL(GPUDevice); LOG_CHANNEL(GPUDevice);
D3D12Texture::D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, D3D12Texture::D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
DXGI_FORMAT dxgi_format, ComPtr<ID3D12Resource> resource, Flags flags, DXGI_FORMAT dxgi_format, ComPtr<ID3D12Resource> resource,
ComPtr<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor, ComPtr<D3D12MA::Allocation> allocation, const D3D12DescriptorHandle& srv_descriptor,
const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& uav_descriptor, const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& uav_descriptor,
WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state) WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state)
: GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels), : GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
static_cast<u8>(samples), type, format), static_cast<u8>(samples), type, format, flags),
m_resource(std::move(resource)), m_allocation(std::move(allocation)), m_srv_descriptor(srv_descriptor), m_resource(std::move(resource)), m_allocation(std::move(allocation)), m_srv_descriptor(srv_descriptor),
m_write_descriptor(write_descriptor), m_uav_descriptor(uav_descriptor), m_dxgi_format(dxgi_format), m_write_descriptor(write_descriptor), m_uav_descriptor(uav_descriptor), m_dxgi_format(dxgi_format),
m_resource_state(resource_state), m_write_descriptor_type(wdtype) m_resource_state(resource_state), m_write_descriptor_type(wdtype)
@ -37,9 +37,10 @@ D3D12Texture::~D3D12Texture()
std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data /* = nullptr */, u32 data_stride /* = 0 */) GPUTexture::Flags flags, const void* data /* = nullptr */,
u32 data_stride /* = 0 */, Error* error /* = nullptr */)
{ {
if (!GPUTexture::ValidateConfig(width, height, layers, levels, samples, type, format)) if (!GPUTexture::ValidateConfig(width, height, layers, levels, samples, type, format, flags, error))
return {}; return {};
const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(format); const D3DCommon::DXGIFormatMapping& fm = D3DCommon::GetFormatMapping(format);
@ -64,7 +65,6 @@ std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u3
switch (type) switch (type)
{ {
case GPUTexture::Type::Texture: case GPUTexture::Type::Texture:
case GPUTexture::Type::DynamicTexture:
{ {
desc.Flags = D3D12_RESOURCE_FLAG_NONE; desc.Flags = D3D12_RESOURCE_FLAG_NONE;
state = D3D12_RESOURCE_STATE_COPY_DEST; state = D3D12_RESOURCE_STATE_COPY_DEST;
@ -92,18 +92,20 @@ std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u3
} }
break; break;
case GPUTexture::Type::RWTexture: DefaultCaseIsUnreachable();
{ }
DebugAssert(levels == 1);
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
optimized_clear_value.Format = fm.rtv_format;
state = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
}
break;
default: if ((flags & GPUTexture::Flags::AllowBindAsImage) != GPUTexture::Flags::None)
return {}; {
DebugAssert(levels == 1);
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
}
if ((flags & GPUTexture::Flags::AllowGenerateMipmaps) != GPUTexture::Flags::None)
{
// requires RTs since we need to draw the mips
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
} }
ComPtr<ID3D12Resource> resource; ComPtr<ID3D12Resource> resource;
@ -115,10 +117,7 @@ std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u3
allocation.GetAddressOf(), IID_PPV_ARGS(resource.GetAddressOf())); allocation.GetAddressOf(), IID_PPV_ARGS(resource.GetAddressOf()));
if (FAILED(hr)) [[unlikely]] if (FAILED(hr)) [[unlikely]]
{ {
// OOM isn't fatal. Error::SetHResult(error, "CreateResource() failed: ", hr);
if (hr != E_OUTOFMEMORY)
ERROR_LOG("Create texture failed: 0x{:08X}", static_cast<unsigned>(hr));
return {}; return {};
} }
@ -126,16 +125,19 @@ std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u3
D3D12Texture::WriteDescriptorType write_descriptor_type = D3D12Texture::WriteDescriptorType::None; D3D12Texture::WriteDescriptorType write_descriptor_type = D3D12Texture::WriteDescriptorType::None;
if (fm.srv_format != DXGI_FORMAT_UNKNOWN) if (fm.srv_format != DXGI_FORMAT_UNKNOWN)
{ {
if (!CreateSRVDescriptor(resource.Get(), layers, levels, samples, fm.srv_format, &srv_descriptor)) if (!CreateSRVDescriptor(resource.Get(), layers, levels, samples, fm.srv_format, &srv_descriptor, error))
return {}; return {};
} }
switch (type) switch (type)
{ {
case GPUTexture::Type::Texture:
break;
case GPUTexture::Type::RenderTarget: case GPUTexture::Type::RenderTarget:
{ {
write_descriptor_type = D3D12Texture::WriteDescriptorType::RTV; write_descriptor_type = D3D12Texture::WriteDescriptorType::RTV;
if (!CreateRTVDescriptor(resource.Get(), samples, fm.rtv_format, &write_descriptor)) if (!CreateRTVDescriptor(resource.Get(), samples, fm.rtv_format, &write_descriptor, error))
{ {
m_descriptor_heap_manager.Free(&srv_descriptor); m_descriptor_heap_manager.Free(&srv_descriptor);
return {}; return {};
@ -146,7 +148,7 @@ std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u3
case GPUTexture::Type::DepthStencil: case GPUTexture::Type::DepthStencil:
{ {
write_descriptor_type = D3D12Texture::WriteDescriptorType::DSV; write_descriptor_type = D3D12Texture::WriteDescriptorType::DSV;
if (!CreateDSVDescriptor(resource.Get(), samples, fm.dsv_format, &write_descriptor)) if (!CreateDSVDescriptor(resource.Get(), samples, fm.dsv_format, &write_descriptor, error))
{ {
m_descriptor_heap_manager.Free(&srv_descriptor); m_descriptor_heap_manager.Free(&srv_descriptor);
return {}; return {};
@ -154,30 +156,23 @@ std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u3
} }
break; break;
case GPUTexture::Type::RWTexture: DefaultCaseIsUnreachable();
}
if ((flags & GPUTexture::Flags::AllowBindAsImage) != GPUTexture::Flags::None)
{
if (!CreateUAVDescriptor(resource.Get(), samples, fm.srv_format, &uav_descriptor, error))
{ {
write_descriptor_type = D3D12Texture::WriteDescriptorType::RTV; if (write_descriptor_type != D3D12Texture::WriteDescriptorType::None)
if (!CreateRTVDescriptor(resource.Get(), samples, fm.rtv_format, &write_descriptor))
{
m_descriptor_heap_manager.Free(&srv_descriptor);
return {};
}
if (!CreateUAVDescriptor(resource.Get(), samples, fm.srv_format, &uav_descriptor))
{
m_descriptor_heap_manager.Free(&write_descriptor); m_descriptor_heap_manager.Free(&write_descriptor);
m_descriptor_heap_manager.Free(&srv_descriptor);
return {};
}
}
break;
default: m_descriptor_heap_manager.Free(&srv_descriptor);
break; return {};
}
} }
std::unique_ptr<D3D12Texture> tex(new D3D12Texture( std::unique_ptr<D3D12Texture> tex(new D3D12Texture(
width, height, layers, levels, samples, type, format, fm.resource_format, std::move(resource), width, height, layers, levels, samples, type, format, flags, fm.resource_format, std::move(resource),
std::move(allocation), srv_descriptor, write_descriptor, uav_descriptor, write_descriptor_type, state)); std::move(allocation), srv_descriptor, write_descriptor, uav_descriptor, write_descriptor_type, state));
if (data) if (data)
@ -190,11 +185,11 @@ std::unique_ptr<GPUTexture> D3D12Device::CreateTexture(u32 width, u32 height, u3
} }
bool D3D12Device::CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format, bool D3D12Device::CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format,
D3D12DescriptorHandle* dh) D3D12DescriptorHandle* dh, Error* error)
{ {
if (!m_descriptor_heap_manager.Allocate(dh)) if (!m_descriptor_heap_manager.Allocate(dh))
{ {
ERROR_LOG("Failed to allocate SRV descriptor"); Error::SetStringView(error, "Failed to allocate SRV descriptor");
return false; return false;
} }
@ -233,11 +228,11 @@ bool D3D12Device::CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32
} }
bool D3D12Device::CreateRTVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, bool D3D12Device::CreateRTVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format,
D3D12DescriptorHandle* dh) D3D12DescriptorHandle* dh, Error* error)
{ {
if (!m_rtv_heap_manager.Allocate(dh)) if (!m_rtv_heap_manager.Allocate(dh))
{ {
ERROR_LOG("Failed to allocate SRV descriptor"); Error::SetStringView(error, "Failed to allocate SRV descriptor");
return false; return false;
} }
@ -248,11 +243,11 @@ bool D3D12Device::CreateRTVDescriptor(ID3D12Resource* resource, u32 samples, DXG
} }
bool D3D12Device::CreateDSVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, bool D3D12Device::CreateDSVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format,
D3D12DescriptorHandle* dh) D3D12DescriptorHandle* dh, Error* error)
{ {
if (!m_dsv_heap_manager.Allocate(dh)) if (!m_dsv_heap_manager.Allocate(dh))
{ {
ERROR_LOG("Failed to allocate SRV descriptor"); Error::SetStringView(error, "Failed to allocate SRV descriptor");
return false; return false;
} }
@ -263,11 +258,11 @@ bool D3D12Device::CreateDSVDescriptor(ID3D12Resource* resource, u32 samples, DXG
} }
bool D3D12Device::CreateUAVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, bool D3D12Device::CreateUAVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format,
D3D12DescriptorHandle* dh) D3D12DescriptorHandle* dh, Error* error)
{ {
if (!m_descriptor_heap_manager.Allocate(dh)) if (!m_descriptor_heap_manager.Allocate(dh))
{ {
ERROR_LOG("Failed to allocate UAV descriptor"); Error::SetStringView(error, "Failed to allocate UAV descriptor");
return false; return false;
} }
@ -334,9 +329,9 @@ void D3D12Texture::Destroy(bool defer)
ID3D12GraphicsCommandList4* D3D12Texture::GetCommandBufferForUpdate() ID3D12GraphicsCommandList4* D3D12Texture::GetCommandBufferForUpdate()
{ {
D3D12Device& dev = D3D12Device::GetInstance(); D3D12Device& dev = D3D12Device::GetInstance();
if ((m_type != Type::Texture && m_type != Type::DynamicTexture) || m_use_fence_counter == dev.GetCurrentFenceValue()) if (m_type != Type::Texture || m_use_fence_counter == dev.GetCurrentFenceValue())
{ {
// Console.WriteLn("Texture update within frame, can't use do beforehand"); // DEV_LOG("Texture update within frame, can't use do beforehand");
if (dev.InRenderPass()) if (dev.InRenderPass())
dev.EndRenderPass(); dev.EndRenderPass();
return dev.GetCommandList(); return dev.GetCommandList();
@ -562,6 +557,28 @@ void D3D12Texture::Unmap()
m_map_level = 0; m_map_level = 0;
} }
void D3D12Texture::GenerateMipmaps()
{
Panic("Not implemented");
for (u32 layer = 0; layer < m_layers; layer++)
{
for (u32 dst_level = 1; dst_level < m_levels; dst_level++)
{
const u32 src_level = dst_level - 1;
const u32 src_width = std::max<u32>(m_width >> src_level, 1u);
const u32 src_height = std::max<u32>(m_height >> src_level, 1u);
const u32 dst_width = std::max<u32>(m_width >> dst_level, 1u);
const u32 dst_height = std::max<u32>(m_height >> dst_level, 1u);
D3D12Device::GetInstance().RenderTextureMipmap(this, dst_level, dst_width, dst_height, src_level, src_width,
src_height);
}
}
SetUseFenceValue(D3D12Device::GetInstance().GetCurrentFenceValue());
}
void D3D12Texture::CommitClear() void D3D12Texture::CommitClear()
{ {
if (m_state != GPUTexture::State::Cleared) if (m_state != GPUTexture::State::Cleared)
@ -685,7 +702,7 @@ void D3D12Sampler::SetDebugName(std::string_view name)
{ {
} }
D3D12DescriptorHandle D3D12Device::GetSampler(const GPUSampler::Config& config) D3D12DescriptorHandle D3D12Device::GetSampler(const GPUSampler::Config& config, Error* error)
{ {
const auto it = m_sampler_map.find(config.key); const auto it = m_sampler_map.find(config.key);
if (it != m_sampler_map.end()) if (it != m_sampler_map.end())
@ -730,8 +747,10 @@ D3D12DescriptorHandle D3D12Device::GetSampler(const GPUSampler::Config& config)
} }
D3D12DescriptorHandle handle; D3D12DescriptorHandle handle;
if (m_sampler_heap_manager.Allocate(&handle)) if (m_sampler_heap_manager.Allocate(&handle)) [[likely]]
m_device->CreateSampler(&desc, handle); m_device->CreateSampler(&desc, handle);
else
Error::SetStringView(error, "Failed to allocate sampler handle.");
m_sampler_map.emplace(config.key, handle); m_sampler_map.emplace(config.key, handle);
return handle; return handle;
@ -747,9 +766,9 @@ void D3D12Device::DestroySamplers()
m_sampler_map.clear(); m_sampler_map.clear();
} }
std::unique_ptr<GPUSampler> D3D12Device::CreateSampler(const GPUSampler::Config& config) std::unique_ptr<GPUSampler> D3D12Device::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */)
{ {
const D3D12DescriptorHandle handle = GetSampler(config); const D3D12DescriptorHandle handle = GetSampler(config, error);
if (!handle) if (!handle)
return {}; return {};
@ -765,21 +784,20 @@ D3D12TextureBuffer::~D3D12TextureBuffer()
Destroy(true); Destroy(true);
} }
bool D3D12TextureBuffer::Create(D3D12Device& dev) bool D3D12TextureBuffer::Create(D3D12Device& dev, Error* error)
{ {
static constexpr std::array<DXGI_FORMAT, static_cast<u8>(GPUTextureBuffer::Format::MaxCount)> format_mapping = {{ static constexpr std::array<DXGI_FORMAT, static_cast<u8>(GPUTextureBuffer::Format::MaxCount)> format_mapping = {{
DXGI_FORMAT_R16_UINT, // R16UI DXGI_FORMAT_R16_UINT, // R16UI
}}; }};
Error error; if (!m_buffer.Create(GetSizeInBytes(), error)) [[unlikely]]
if (!m_buffer.Create(GetSizeInBytes(), &error)) [[unlikely]]
{
ERROR_LOG("Failed to create stream buffer: {}", error.GetDescription());
return false; return false;
}
if (!dev.GetDescriptorHeapManager().Allocate(&m_descriptor)) [[unlikely]] if (!dev.GetDescriptorHeapManager().Allocate(&m_descriptor)) [[unlikely]]
{
Error::SetStringView(error, "Failed to allocate descriptor.");
return {}; return {};
}
D3D12_SHADER_RESOURCE_VIEW_DESC desc = {format_mapping[static_cast<u8>(m_format)], D3D12_SHADER_RESOURCE_VIEW_DESC desc = {format_mapping[static_cast<u8>(m_format)],
D3D12_SRV_DIMENSION_BUFFER, D3D12_SRV_DIMENSION_BUFFER,
@ -831,11 +849,11 @@ void D3D12TextureBuffer::SetDebugName(std::string_view name)
} }
std::unique_ptr<GPUTextureBuffer> D3D12Device::CreateTextureBuffer(GPUTextureBuffer::Format format, std::unique_ptr<GPUTextureBuffer> D3D12Device::CreateTextureBuffer(GPUTextureBuffer::Format format,
u32 size_in_elements) u32 size_in_elements, Error* error /* = nullptr */)
{ {
std::unique_ptr<D3D12TextureBuffer> tb = std::make_unique<D3D12TextureBuffer>(format, size_in_elements); std::unique_ptr<D3D12TextureBuffer> tb = std::make_unique<D3D12TextureBuffer>(format, size_in_elements);
if (!tb->Create(*this)) if (!tb->Create(*this, error))
tb.reset(); tb.reset();
return tb; return tb;
@ -858,7 +876,8 @@ D3D12DownloadTexture::~D3D12DownloadTexture()
D3D12Device::GetInstance().DeferResourceDestruction(m_allocation.Get(), m_buffer.Get()); D3D12Device::GetInstance().DeferResourceDestruction(m_allocation.Get(), m_buffer.Get());
} }
std::unique_ptr<D3D12DownloadTexture> D3D12DownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format) std::unique_ptr<D3D12DownloadTexture> D3D12DownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format,
Error* error)
{ {
const u32 buffer_size = GetBufferSize(width, height, format, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); const u32 buffer_size = GetBufferSize(width, height, format, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
@ -879,12 +898,12 @@ std::unique_ptr<D3D12DownloadTexture> D3D12DownloadTexture::Create(u32 width, u3
ComPtr<D3D12MA::Allocation> allocation; ComPtr<D3D12MA::Allocation> allocation;
ComPtr<ID3D12Resource> buffer; ComPtr<ID3D12Resource> buffer;
HRESULT hr = D3D12Device::GetInstance().GetAllocator()->CreateResource( const HRESULT hr = D3D12Device::GetInstance().GetAllocator()->CreateResource(
&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, allocation.GetAddressOf(), &allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, allocation.GetAddressOf(),
IID_PPV_ARGS(buffer.GetAddressOf())); IID_PPV_ARGS(buffer.GetAddressOf()));
if (FAILED(hr)) if (FAILED(hr))
{ {
ERROR_LOG("CreateResource() failed with HRESULT {:08X}", hr); Error::SetHResult(error, "CreateResource() failed: ", hr);
return {}; return {};
} }
@ -1015,15 +1034,16 @@ void D3D12DownloadTexture::SetDebugName(std::string_view name)
D3D12::SetObjectName(m_buffer.Get(), name); D3D12::SetObjectName(m_buffer.Get(), name);
} }
std::unique_ptr<GPUDownloadTexture> D3D12Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) std::unique_ptr<GPUDownloadTexture> D3D12Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
Error* error /* = nullptr */)
{ {
return D3D12DownloadTexture::Create(width, height, format); return D3D12DownloadTexture::Create(width, height, format, error);
} }
std::unique_ptr<GPUDownloadTexture> D3D12Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<GPUDownloadTexture> D3D12Device::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, void* memory, size_t memory_size,
u32 memory_stride) u32 memory_stride, Error* error /* = nullptr */)
{ {
ERROR_LOG("D3D12 cannot import memory for download textures"); Error::SetStringView(error, "D3D12 cannot import memory for download textures");
return {}; return {};
} }

View File

@ -40,6 +40,7 @@ public:
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
void Unmap() override; void Unmap() override;
void GenerateMipmaps() override;
void MakeReadyForSampling() override; void MakeReadyForSampling() override;
void SetDebugName(std::string_view name) override; void SetDebugName(std::string_view name) override;
@ -71,7 +72,7 @@ private:
DSV DSV
}; };
D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, D3D12Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, Flags flags,
DXGI_FORMAT dxgi_format, ComPtr<ID3D12Resource> resource, ComPtr<D3D12MA::Allocation> allocation, DXGI_FORMAT dxgi_format, ComPtr<ID3D12Resource> resource, ComPtr<D3D12MA::Allocation> allocation,
const D3D12DescriptorHandle& srv_descriptor, const D3D12DescriptorHandle& write_descriptor, const D3D12DescriptorHandle& srv_descriptor, const D3D12DescriptorHandle& write_descriptor,
const D3D12DescriptorHandle& uav_descriptor, WriteDescriptorType wdtype, const D3D12DescriptorHandle& uav_descriptor, WriteDescriptorType wdtype,
@ -133,7 +134,7 @@ public:
ALWAYS_INLINE const D3D12DescriptorHandle& GetDescriptor() const { return m_descriptor; } ALWAYS_INLINE const D3D12DescriptorHandle& GetDescriptor() const { return m_descriptor; }
bool Create(D3D12Device& dev); bool Create(D3D12Device& dev, Error* error);
void Destroy(bool defer); void Destroy(bool defer);
// Inherited via GPUTextureBuffer // Inherited via GPUTextureBuffer
@ -155,7 +156,7 @@ public:
~D3D12DownloadTexture() override; ~D3D12DownloadTexture() override;
static std::unique_ptr<D3D12DownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format); static std::unique_ptr<D3D12DownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format, Error* error);
void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height, void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height,
u32 src_layer, u32 src_level, bool use_transfer_pitch) override; u32 src_layer, u32 src_level, bool use_transfer_pitch) override;

View File

@ -590,10 +590,10 @@ bool GPUDevice::GetPipelineCacheData(DynamicHeapArray<u8>* data, Error* error)
bool GPUDevice::CreateResources(Error* error) bool GPUDevice::CreateResources(Error* error)
{ {
if (!(m_nearest_sampler = CreateSampler(GPUSampler::GetNearestConfig())) || if (!(m_nearest_sampler = CreateSampler(GPUSampler::GetNearestConfig(), error)) ||
!(m_linear_sampler = CreateSampler(GPUSampler::GetLinearConfig()))) !(m_linear_sampler = CreateSampler(GPUSampler::GetLinearConfig(), error)))
{ {
Error::SetStringView(error, "Failed to create samplers"); Error::AddPrefix(error, "Failed to create samplers: ");
return false; return false;
} }
@ -922,10 +922,15 @@ bool GPUDevice::UpdateImGuiFontTexture()
return true; return true;
} }
Error error;
std::unique_ptr<GPUTexture> new_font = std::unique_ptr<GPUTexture> new_font =
FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8, pixels, pitch); FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8, GPUTexture::Flags::None,
if (!new_font) pixels, pitch, &error);
if (!new_font) [[unlikely]]
{
ERROR_LOG("Failed to create new ImGui font texture: {}", error.GetDescription());
return false; return false;
}
RecycleTexture(std::move(m_imgui_font_texture)); RecycleTexture(std::move(m_imgui_font_texture));
m_imgui_font_texture = std::move(new_font); m_imgui_font_texture = std::move(new_font);
@ -950,12 +955,13 @@ GSVector4i GPUDevice::FlipToLowerLeft(GSVector4i rc, s32 target_height)
bool GPUDevice::IsTexturePoolType(GPUTexture::Type type) bool GPUDevice::IsTexturePoolType(GPUTexture::Type type)
{ {
return (type == GPUTexture::Type::Texture || type == GPUTexture::Type::DynamicTexture); return (type == GPUTexture::Type::Texture);
} }
std::unique_ptr<GPUTexture> GPUDevice::FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> GPUDevice::FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data /*= nullptr*/, u32 data_stride /*= 0*/) GPUTexture::Flags flags, const void* data /* = nullptr */,
u32 data_stride /* = 0 */, Error* error /* = nullptr */)
{ {
std::unique_ptr<GPUTexture> ret; std::unique_ptr<GPUTexture> ret;
@ -966,7 +972,7 @@ std::unique_ptr<GPUTexture> GPUDevice::FetchTexture(u32 width, u32 height, u32 l
static_cast<u8>(samples), static_cast<u8>(samples),
type, type,
format, format,
0u}; flags};
const bool is_texture = IsTexturePoolType(type); const bool is_texture = IsTexturePoolType(type);
TexturePool& pool = is_texture ? m_texture_pool : m_target_pool; TexturePool& pool = is_texture ? m_texture_pool : m_target_pool;
@ -1018,17 +1024,29 @@ std::unique_ptr<GPUTexture> GPUDevice::FetchTexture(u32 width, u32 height, u32 l
} }
} }
ret = CreateTexture(width, height, layers, levels, samples, type, format, data, data_stride); Error create_error;
ret = CreateTexture(width, height, layers, levels, samples, type, format, flags, data, data_stride, &create_error);
if (!ret) [[unlikely]]
{
Error::SetStringFmt(
error ? error : &create_error, "Failed to create {}x{} {} {}: {}", width, height,
GPUTexture::GetFormatName(format),
((type == GPUTexture::Type::RenderTarget) ? "RT" : (type == GPUTexture::Type::DepthStencil ? "DS" : "Texture")),
create_error.TakeDescription());
if (!error)
ERROR_LOG(create_error.GetDescription());
}
return ret; return ret;
} }
std::unique_ptr<GPUTexture, GPUDevice::PooledTextureDeleter> std::unique_ptr<GPUTexture, GPUDevice::PooledTextureDeleter>
GPUDevice::FetchAutoRecycleTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, GPUDevice::FetchAutoRecycleTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type,
GPUTexture::Format format, const void* data /*= nullptr*/, u32 data_stride /*= 0*/, GPUTexture::Format format, GPUTexture::Flags flags, const void* data /* = nullptr */,
bool dynamic /*= false*/) u32 data_stride /* = 0 */, Error* error /* = nullptr */)
{ {
std::unique_ptr<GPUTexture> ret = std::unique_ptr<GPUTexture> ret =
FetchTexture(width, height, layers, levels, samples, type, format, data, data_stride); FetchTexture(width, height, layers, levels, samples, type, format, flags, data, data_stride, error);
return std::unique_ptr<GPUTexture, PooledTextureDeleter>(ret.release()); return std::unique_ptr<GPUTexture, PooledTextureDeleter>(ret.release());
} }
@ -1044,7 +1062,7 @@ void GPUDevice::RecycleTexture(std::unique_ptr<GPUTexture> texture)
static_cast<u8>(texture->GetSamples()), static_cast<u8>(texture->GetSamples()),
texture->GetType(), texture->GetType(),
texture->GetFormat(), texture->GetFormat(),
0u}; texture->GetFlags()};
const bool is_texture = IsTexturePoolType(texture->GetType()); const bool is_texture = IsTexturePoolType(texture->GetType());
TexturePool& pool = is_texture ? m_texture_pool : m_target_pool; TexturePool& pool = is_texture ? m_texture_pool : m_target_pool;
@ -1118,11 +1136,11 @@ void GPUDevice::TrimTexturePool()
} }
bool GPUDevice::ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type, bool GPUDevice::ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
GPUTexture::Format format, bool preserve /* = true */) GPUTexture::Format format, GPUTexture::Flags flags, bool preserve /* = true */)
{ {
GPUTexture* old_tex = tex->get(); GPUTexture* old_tex = tex->get();
DebugAssert(!old_tex || (old_tex->GetLayers() == 1 && old_tex->GetLevels() == 1 && old_tex->GetSamples() == 1)); DebugAssert(!old_tex || (old_tex->GetLayers() == 1 && old_tex->GetLevels() == 1 && old_tex->GetSamples() == 1));
std::unique_ptr<GPUTexture> new_tex = FetchTexture(new_width, new_height, 1, 1, 1, type, format); std::unique_ptr<GPUTexture> new_tex = FetchTexture(new_width, new_height, 1, 1, 1, type, format, flags);
if (!new_tex) [[unlikely]] if (!new_tex) [[unlikely]]
{ {
ERROR_LOG("Failed to create new {}x{} texture", new_width, new_height); ERROR_LOG("Failed to create new {}x{} texture", new_width, new_height);

View File

@ -693,27 +693,28 @@ public:
virtual std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, virtual std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data = nullptr, u32 data_stride = 0) = 0; GPUTexture::Flags flags, const void* data = nullptr,
virtual std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) = 0; u32 data_stride = 0, Error* error = nullptr) = 0;
virtual std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, virtual std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config, Error* error = nullptr) = 0;
u32 size_in_elements) = 0; virtual std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements,
Error* error = nullptr) = 0;
// Texture pooling. // Texture pooling.
std::unique_ptr<GPUTexture> FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, const void* data = nullptr, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags,
u32 data_stride = 0); const void* data = nullptr, u32 data_stride = 0, Error* error = nullptr);
std::unique_ptr<GPUTexture, PooledTextureDeleter> std::unique_ptr<GPUTexture, PooledTextureDeleter>
FetchAutoRecycleTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, FetchAutoRecycleTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type,
GPUTexture::Format format, const void* data = nullptr, u32 data_stride = 0, GPUTexture::Format format, GPUTexture::Flags flags, const void* data = nullptr,
bool dynamic = false); u32 data_stride = 0, Error* error = nullptr);
void RecycleTexture(std::unique_ptr<GPUTexture> texture); void RecycleTexture(std::unique_ptr<GPUTexture> texture);
void PurgeTexturePool(); void PurgeTexturePool();
virtual std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height,
GPUTexture::Format format) = 0;
virtual std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, virtual std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, Error* error = nullptr) = 0;
u32 memory_stride) = 0; virtual std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, u32 memory_stride,
Error* error = nullptr) = 0;
virtual void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, virtual void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) = 0; u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) = 0;
@ -789,7 +790,7 @@ public:
bool UsesLowerLeftOrigin() const; bool UsesLowerLeftOrigin() const;
static GSVector4i FlipToLowerLeft(GSVector4i rc, s32 target_height); static GSVector4i FlipToLowerLeft(GSVector4i rc, s32 target_height);
bool ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type, bool ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
GPUTexture::Format format, bool preserve = true); GPUTexture::Format format, GPUTexture::Flags flags, bool preserve = true);
virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0; virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
@ -863,7 +864,7 @@ private:
u8 samples; u8 samples;
GPUTexture::Type type; GPUTexture::Type type;
GPUTexture::Format format; GPUTexture::Format format;
u8 pad; GPUTexture::Flags flags;
ALWAYS_INLINE bool operator==(const TexturePoolKey& rhs) const ALWAYS_INLINE bool operator==(const TexturePoolKey& rhs) const
{ {

View File

@ -92,7 +92,7 @@ template<typename FBOType, FBOType (*FactoryFunc)(GPUTexture* const* rts, u32 nu
void (*DestroyFunc)(FBOType fbo)> void (*DestroyFunc)(FBOType fbo)>
void GPUFramebufferManager<FBOType, FactoryFunc, DestroyFunc>::RemoveRTReferences(const GPUTexture* tex) void GPUFramebufferManager<FBOType, FactoryFunc, DestroyFunc>::RemoveRTReferences(const GPUTexture* tex)
{ {
DebugAssert(tex->IsRenderTarget() || tex->IsRWTexture()); DebugAssert(tex->IsRenderTarget());
for (auto it = m_map.begin(); it != m_map.end();) for (auto it = m_map.begin(); it != m_map.end();)
{ {
if (!it->first.ContainsRT(tex)) if (!it->first.ContainsRT(tex))

View File

@ -7,14 +7,12 @@
#include "common/align.h" #include "common/align.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/bitutils.h" #include "common/bitutils.h"
#include "common/log.h" #include "common/error.h"
#include "common/string_util.h" #include "common/string_util.h"
LOG_CHANNEL(GPUTexture); GPUTexture::GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format, Flags flags)
GPUTexture::GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format)
: m_width(width), m_height(height), m_layers(layers), m_levels(levels), m_samples(samples), m_type(type), : m_width(width), m_height(height), m_layers(layers), m_levels(levels), m_samples(samples), m_type(type),
m_format(format) m_format(format), m_flags(flags)
{ {
GPUDevice::s_total_vram_usage += GetVRAMUsage(); GPUDevice::s_total_vram_usage += GetVRAMUsage();
} }
@ -119,6 +117,12 @@ u32 GPUTexture::CalcUploadSize(Format format, u32 height, u32 pitch)
return pitch * ((static_cast<u32>(height) + (block_size - 1)) / block_size); return pitch * ((static_cast<u32>(height) + (block_size - 1)) / block_size);
} }
u32 GPUTexture::GetFullMipmapCount(u32 width, u32 height)
{
const u32 max_dim = Common::PreviousPow2(std::max(width, height));
return (std::countr_zero(max_dim) + 1);
}
std::array<float, 4> GPUTexture::GetUNormClearColor() const std::array<float, 4> GPUTexture::GetUNormClearColor() const
{ {
return GPUDevice::RGBA8ToFloat(m_clear_value.color); return GPUDevice::RGBA8ToFloat(m_clear_value.color);
@ -192,25 +196,28 @@ bool GPUTexture::IsCompressedFormat(Format format)
return false; return false;
} }
bool GPUTexture::ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format) bool GPUTexture::ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
Flags flags, Error* error)
{ {
if (width > MAX_WIDTH || height > MAX_HEIGHT || layers > MAX_LAYERS || levels > MAX_LEVELS || samples > MAX_SAMPLES) if (width == 0 || width > MAX_WIDTH || height == 0 || height > MAX_HEIGHT || layers == 0 || layers > MAX_LAYERS ||
levels == 0 || levels > MAX_LEVELS || samples == 0 || samples > MAX_SAMPLES)
{ {
ERROR_LOG("Invalid dimensions: {}x{}x{} {} {}.", width, height, layers, levels, samples); Error::SetStringFmt(error, "Invalid dimensions: {}x{}x{} {} {}.", width, height, layers, levels, samples);
return false; return false;
} }
const u32 max_texture_size = g_gpu_device->GetMaxTextureSize(); const u32 max_texture_size = g_gpu_device->GetMaxTextureSize();
if (width > max_texture_size || height > max_texture_size) if (width > max_texture_size || height > max_texture_size)
{ {
ERROR_LOG("Texture width ({}) or height ({}) exceeds max texture size ({}).", width, height, max_texture_size); Error::SetStringFmt(error, "Texture width ({}) or height ({}) exceeds max texture size ({}).", width, height,
max_texture_size);
return false; return false;
} }
const u32 max_samples = g_gpu_device->GetMaxMultisamples(); const u32 max_samples = g_gpu_device->GetMaxMultisamples();
if (samples > max_samples) if (samples > max_samples)
{ {
ERROR_LOG("Texture samples ({}) exceeds max samples ({}).", samples, max_samples); Error::SetStringFmt(error, "Texture samples ({}) exceeds max samples ({}).", samples, max_samples);
return false; return false;
} }
@ -218,25 +225,45 @@ bool GPUTexture::ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u
{ {
if (levels > 1) if (levels > 1)
{ {
ERROR_LOG("Multisampled textures can't have mip levels."); Error::SetStringView(error, "Multisampled textures can't have mip levels.");
return false; return false;
} }
else if (type != Type::RenderTarget && type != Type::DepthStencil) else if (type != Type::RenderTarget && type != Type::DepthStencil)
{ {
ERROR_LOG("Multisampled textures must be render targets or depth stencil targets."); Error::SetStringView(error, "Multisampled textures must be render targets or depth stencil targets.");
return false; return false;
} }
} }
if (layers > 1 && type != Type::Texture && type != Type::DynamicTexture) if (layers > 1 && type != Type::Texture)
{ {
ERROR_LOG("Texture arrays are not supported on targets."); Error::SetStringView(error, "Texture arrays are not supported on targets.");
return false; return false;
} }
if (levels > 1 && type != Type::Texture && type != Type::DynamicTexture) if (levels > 1 && type != Type::Texture)
{ {
ERROR_LOG("Mipmaps are not supported on targets."); Error::SetStringView(error, "Mipmaps are not supported on targets.");
return false;
}
if ((flags & Flags::AllowGenerateMipmaps) != Flags::None && levels <= 1)
{
Error::SetStringView(error, "Allow generate mipmaps requires >1 level.");
return false;
}
if ((flags & Flags::AllowBindAsImage) != Flags::None &&
((type != Type::Texture && type != Type::RenderTarget) || levels > 1))
{
Error::SetStringView(error, "Bind as image is not allowed on depth or mipmapped targets.");
return false;
}
if ((flags & Flags::AllowMap) != Flags::None &&
(type != Type::Texture || (flags & Flags::AllowGenerateMipmaps) != Flags::None))
{
Error::SetStringView(error, "Allow map is not supported on targets.");
return false; return false;
} }
@ -331,7 +358,6 @@ bool GPUTexture::ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u3
} }
default: default:
[[unlikely]] ERROR_LOG("Unknown pixel format {}", static_cast<u32>(format));
return false; return false;
} }
} }

View File

@ -11,6 +11,8 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
class Error;
class GPUTexture class GPUTexture
{ {
public: public:
@ -25,12 +27,9 @@ public:
enum class Type : u8 enum class Type : u8
{ {
Unknown, Texture,
RenderTarget, RenderTarget,
DepthStencil, DepthStencil,
Texture,
DynamicTexture,
RWTexture,
}; };
enum class Format : u8 enum class Format : u8
@ -70,6 +69,15 @@ public:
Invalidated Invalidated
}; };
enum class Flags : u8
{
None = 0,
AllowMap = (1 << 0),
AllowBindAsImage = (1 << 2),
AllowGenerateMipmaps = (1 << 3),
AllowMSAAResolveTarget = (1 << 4),
};
union ClearValue union ClearValue
{ {
u32 color; u32 color;
@ -81,20 +89,22 @@ public:
virtual ~GPUTexture(); virtual ~GPUTexture();
static const char* GetFormatName(Format format); static const char* GetFormatName(Format format);
static u32 GetPixelSize(GPUTexture::Format format); static u32 GetPixelSize(Format format);
static bool IsDepthFormat(GPUTexture::Format format); static bool IsDepthFormat(Format format);
static bool IsDepthStencilFormat(GPUTexture::Format format); static bool IsDepthStencilFormat(Format format);
static bool IsCompressedFormat(Format format); static bool IsCompressedFormat(Format format);
static u32 GetCompressedBytesPerBlock(Format format); static u32 GetCompressedBytesPerBlock(Format format);
static u32 GetCompressedBlockSize(Format format); static u32 GetCompressedBlockSize(Format format);
static u32 CalcUploadPitch(Format format, u32 width); static u32 CalcUploadPitch(Format format, u32 width);
static u32 CalcUploadRowLengthFromPitch(Format format, u32 pitch); static u32 CalcUploadRowLengthFromPitch(Format format, u32 pitch);
static u32 CalcUploadSize(Format format, u32 height, u32 pitch); static u32 CalcUploadSize(Format format, u32 height, u32 pitch);
static u32 GetFullMipmapCount(u32 width, u32 height);
static bool ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format); static bool ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
Flags flags, Error* error);
static bool ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32& texture_data_stride, static bool ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32& texture_data_stride,
GPUTexture::Format format); Format format);
static void FlipTextureDataRGBA8(u32 width, u32 height, u8* texture_data, u32 texture_data_stride); static void FlipTextureDataRGBA8(u32 width, u32 height, u8* texture_data, u32 texture_data_stride);
ALWAYS_INLINE u32 GetWidth() const { return m_width; } ALWAYS_INLINE u32 GetWidth() const { return m_width; }
@ -104,6 +114,8 @@ public:
ALWAYS_INLINE u32 GetSamples() const { return m_samples; } ALWAYS_INLINE u32 GetSamples() const { return m_samples; }
ALWAYS_INLINE Type GetType() const { return m_type; } ALWAYS_INLINE Type GetType() const { return m_type; }
ALWAYS_INLINE Format GetFormat() const { return m_format; } ALWAYS_INLINE Format GetFormat() const { return m_format; }
ALWAYS_INLINE Flags GetFlags() const { return m_flags; }
ALWAYS_INLINE bool HasFlag(Flags flag) const { return ((static_cast<u8>(m_flags) & static_cast<u8>(flag)) != 0); }
ALWAYS_INLINE GSVector4i GetRect() const ALWAYS_INLINE GSVector4i GetRect() const
{ {
return GSVector4i(0, 0, static_cast<s32>(m_width), static_cast<s32>(m_height)); return GSVector4i(0, 0, static_cast<s32>(m_width), static_cast<s32>(m_height));
@ -121,15 +133,13 @@ public:
ALWAYS_INLINE bool IsDirty() const { return (m_state == State::Dirty); } ALWAYS_INLINE bool IsDirty() const { return (m_state == State::Dirty); }
ALWAYS_INLINE bool IsClearedOrInvalidated() const { return (m_state != State::Dirty); } ALWAYS_INLINE bool IsClearedOrInvalidated() const { return (m_state != State::Dirty); }
ALWAYS_INLINE bool IsTexture() const { return (m_type == Type::Texture); }
ALWAYS_INLINE bool IsRenderTarget() const { return (m_type == Type::RenderTarget); }
ALWAYS_INLINE bool IsDepthStencil() const { return (m_type == Type::DepthStencil); }
ALWAYS_INLINE bool IsRenderTargetOrDepthStencil() const ALWAYS_INLINE bool IsRenderTargetOrDepthStencil() const
{ {
return (m_type >= Type::RenderTarget && m_type <= Type::DepthStencil); return (m_type >= Type::RenderTarget && m_type <= Type::DepthStencil);
} }
ALWAYS_INLINE bool IsRenderTarget() const { return (m_type == Type::RenderTarget); }
ALWAYS_INLINE bool IsDepthStencil() const { return (m_type == Type::DepthStencil); }
ALWAYS_INLINE bool IsTexture() const { return (m_type == Type::Texture || m_type == Type::DynamicTexture); }
ALWAYS_INLINE bool IsDynamicTexture() const { return (m_type == Type::DynamicTexture); }
ALWAYS_INLINE bool IsRWTexture() const { return (m_type == Type::RWTexture); }
ALWAYS_INLINE const ClearValue& GetClearValue() const { return m_clear_value; } ALWAYS_INLINE const ClearValue& GetClearValue() const { return m_clear_value; }
ALWAYS_INLINE u32 GetClearColor() const { return m_clear_value.color; } ALWAYS_INLINE u32 GetClearColor() const { return m_clear_value.color; }
@ -162,27 +172,32 @@ public:
virtual bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) = 0; virtual bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) = 0;
virtual void Unmap() = 0; virtual void Unmap() = 0;
virtual void GenerateMipmaps() = 0;
// Instructs the backend that we're finished rendering to this texture. It may transition it to a new layout. // Instructs the backend that we're finished rendering to this texture. It may transition it to a new layout.
virtual void MakeReadyForSampling(); virtual void MakeReadyForSampling();
virtual void SetDebugName(std::string_view name) = 0; virtual void SetDebugName(std::string_view name) = 0;
protected: protected:
GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format); GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format, Flags flags);
u16 m_width = 0; u16 m_width = 0;
u16 m_height = 0; u16 m_height = 0;
u8 m_layers = 0; u8 m_layers = 0;
u8 m_levels = 0; u8 m_levels = 0;
u8 m_samples = 0; u8 m_samples = 0;
Type m_type = Type::Unknown; Type m_type = Type::Texture;
Format m_format = Format::Unknown; Format m_format = Format::Unknown;
Flags m_flags = Flags::None;
State m_state = State::Dirty; State m_state = State::Dirty;
ClearValue m_clear_value = {}; ClearValue m_clear_value = {};
}; };
IMPLEMENT_ENUM_CLASS_BITWISE_OPERATORS(GPUTexture::Flags);
class GPUDownloadTexture class GPUDownloadTexture
{ {
public: public:

View File

@ -290,9 +290,9 @@ const std::shared_ptr<GPUTexture>& ImGuiFullscreen::GetPlaceholderTexture()
std::unique_ptr<GPUTexture> ImGuiFullscreen::CreateTextureFromImage(const RGBA8Image& image) std::unique_ptr<GPUTexture> ImGuiFullscreen::CreateTextureFromImage(const RGBA8Image& image)
{ {
std::unique_ptr<GPUTexture> ret = std::unique_ptr<GPUTexture> ret = g_gpu_device->CreateTexture(
g_gpu_device->CreateTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8,
GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch()); GPUTexture::Flags::None, image.GetPixels(), image.GetPitch());
if (!ret) [[unlikely]] if (!ret) [[unlikely]]
ERROR_LOG("Failed to upload {}x{} RGBA8Image to GPU", image.GetWidth(), image.GetHeight()); ERROR_LOG("Failed to upload {}x{} RGBA8Image to GPU", image.GetWidth(), image.GetHeight());
return ret; return ret;
@ -368,7 +368,7 @@ std::shared_ptr<GPUTexture> ImGuiFullscreen::UploadTexture(std::string_view path
{ {
std::unique_ptr<GPUTexture> texture = std::unique_ptr<GPUTexture> texture =
g_gpu_device->FetchTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, g_gpu_device->FetchTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch()); GPUTexture::Format::RGBA8, GPUTexture::Flags::None, image.GetPixels(), image.GetPitch());
if (!texture) if (!texture)
{ {
ERROR_LOG("Failed to create {}x{} texture for resource", image.GetWidth(), image.GetHeight()); ERROR_LOG("Failed to create {}x{} texture for resource", image.GetWidth(), image.GetHeight());

View File

@ -1241,19 +1241,21 @@ void ImGuiManager::UpdateSoftwareCursorTexture(u32 index)
return; return;
} }
Error error;
RGBA8Image image; RGBA8Image image;
if (!image.LoadFromFile(sc.image_path.c_str())) if (!image.LoadFromFile(sc.image_path.c_str(), &error))
{ {
ERROR_LOG("Failed to load software cursor {} image '{}'", index, sc.image_path); ERROR_LOG("Failed to load software cursor {} image '{}': {}", index, sc.image_path, error.GetDescription());
return; return;
} }
g_gpu_device->RecycleTexture(std::move(sc.texture)); g_gpu_device->RecycleTexture(std::move(sc.texture));
sc.texture = g_gpu_device->FetchTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, sc.texture = g_gpu_device->FetchTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch()); GPUTexture::Format::RGBA8, GPUTexture::Flags::None, image.GetPixels(),
image.GetPitch(), &error);
if (!sc.texture) if (!sc.texture)
{ {
ERROR_LOG("Failed to upload {}x{} software cursor {} image '{}'", image.GetWidth(), image.GetHeight(), index, ERROR_LOG("Failed to upload {}x{} software cursor {} image '{}': {}", image.GetWidth(), image.GetHeight(), index,
sc.image_path); sc.image_path, error.GetDescription());
return; return;
} }

View File

@ -244,7 +244,7 @@ GPUTexture* MediaCaptureBase::GetRenderTexture()
return m_render_texture.get(); return m_render_texture.get();
m_render_texture = g_gpu_device->CreateTexture(m_video_width, m_video_height, 1, 1, 1, GPUTexture::Type::RenderTarget, m_render_texture = g_gpu_device->CreateTexture(m_video_width, m_video_height, 1, 1, 1, GPUTexture::Type::RenderTarget,
m_video_render_texture_format); m_video_render_texture_format, GPUTexture::Flags::None);
if (!m_render_texture) [[unlikely]] if (!m_render_texture) [[unlikely]]
{ {
ERROR_LOG("Failed to create {}x{} render texture.", m_video_width, m_video_height); ERROR_LOG("Failed to create {}x{} render texture.", m_video_width, m_video_height);

View File

@ -120,6 +120,7 @@ public:
void Unmap() override; void Unmap() override;
void MakeReadyForSampling() override; void MakeReadyForSampling() override;
void GenerateMipmaps() override;
void SetDebugName(std::string_view name) override; void SetDebugName(std::string_view name) override;
@ -128,7 +129,7 @@ public:
private: private:
MetalTexture(id<MTLTexture> texture, u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, MetalTexture(id<MTLTexture> texture, u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type,
Format format); Format format, Flags flags);
id<MTLTexture> m_texture; id<MTLTexture> m_texture;
@ -150,7 +151,7 @@ public:
~MetalDownloadTexture() override; ~MetalDownloadTexture() override;
static std::unique_ptr<MetalDownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format, void* memory, static std::unique_ptr<MetalDownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format, void* memory,
size_t memory_size, u32 memory_stride); size_t memory_size, u32 memory_stride, Error* error);
void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height, void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height,
u32 src_layer, u32 src_level, bool use_transfer_pitch) override; u32 src_layer, u32 src_level, bool use_transfer_pitch) override;
@ -180,7 +181,7 @@ public:
ALWAYS_INLINE id<MTLBuffer> GetMTLBuffer() const { return m_buffer.GetBuffer(); } ALWAYS_INLINE id<MTLBuffer> GetMTLBuffer() const { return m_buffer.GetBuffer(); }
bool CreateBuffer(id<MTLDevice> device); bool CreateBuffer(id<MTLDevice> device, Error* error);
// Inherited via GPUTextureBuffer // Inherited via GPUTextureBuffer
void* Map(u32 required_elements) override; void* Map(u32 required_elements) override;
@ -234,15 +235,18 @@ public:
std::optional<bool> exclusive_fullscreen_control, std::optional<bool> exclusive_fullscreen_control,
Error* error) override; Error* error) override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags,
const void* data = nullptr, u32 data_stride = 0) override; const void* data = nullptr, u32 data_stride = 0,
std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) override; Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements) override; std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config, Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements,
Error* error = nullptr) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, Error* error = nullptr) override;
u32 memory_stride) override; std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, u32 memory_stride,
Error* error = nullptr) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override; bool SupportsTextureFormat(GPUTexture::Format format) const override;
void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
@ -325,7 +329,7 @@ private:
static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024; static constexpr u32 INDEX_BUFFER_SIZE = 4 * 1024 * 1024;
static constexpr u32 UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024; static constexpr u32 UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024;
static constexpr u32 UNIFORM_BUFFER_ALIGNMENT = 256; static constexpr u32 UNIFORM_BUFFER_ALIGNMENT = 256;
static constexpr u32 TEXTURE_STREAM_BUFFER_SIZE = 32 /*16*/ * 1024 * 1024; // TODO reduce after separate allocations static constexpr u32 TEXTURE_STREAM_BUFFER_SIZE = 64 * 1024 * 1024; // TODO reduce after separate allocations
static constexpr u8 NUM_TIMESTAMP_QUERIES = 3; static constexpr u8 NUM_TIMESTAMP_QUERIES = 3;
using DepthStateMap = std::unordered_map<u8, id<MTLDepthStencilState>>; using DepthStateMap = std::unordered_map<u8, id<MTLDepthStencilState>>;
@ -377,7 +381,7 @@ private:
void RenderBlankFrame(MetalSwapChain* swap_chain); void RenderBlankFrame(MetalSwapChain* swap_chain);
bool CreateBuffers(); bool CreateBuffers(Error* error);
void DestroyBuffers(); void DestroyBuffers();
bool IsRenderTargetBound(const GPUTexture* tex) const; bool IsRenderTargetBound(const GPUTexture* tex) const;

View File

@ -347,11 +347,8 @@ bool MetalDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Feature
return false; return false;
} }
if (!CreateBuffers()) if (!CreateBuffers(error))
{
Error::SetStringView(error, "Failed to create buffers.");
return false; return false;
}
return true; return true;
} }
@ -575,13 +572,14 @@ std::string MetalDevice::GetDriverInfo() const
} }
} }
bool MetalDevice::CreateBuffers() bool MetalDevice::CreateBuffers(Error* error)
{ {
if (!m_vertex_buffer.Create(m_device, VERTEX_BUFFER_SIZE) || !m_index_buffer.Create(m_device, INDEX_BUFFER_SIZE) || if (!m_vertex_buffer.Create(m_device, VERTEX_BUFFER_SIZE, error) ||
!m_uniform_buffer.Create(m_device, UNIFORM_BUFFER_SIZE) || !m_index_buffer.Create(m_device, INDEX_BUFFER_SIZE, error) ||
!m_texture_upload_buffer.Create(m_device, TEXTURE_STREAM_BUFFER_SIZE)) !m_uniform_buffer.Create(m_device, UNIFORM_BUFFER_SIZE, error) ||
!m_texture_upload_buffer.Create(m_device, TEXTURE_STREAM_BUFFER_SIZE, error))
{ {
ERROR_LOG("Failed to create vertex/index/uniform buffers."); Error::AddPrefix(error, "Failed to create vertex/index/uniform buffers: ");
return false; return false;
} }
@ -980,8 +978,8 @@ std::unique_ptr<GPUPipeline> MetalDevice::CreatePipeline(const GPUPipeline::Comp
} }
MetalTexture::MetalTexture(id<MTLTexture> texture, u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, MetalTexture::MetalTexture(id<MTLTexture> texture, u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type,
Format format) Format format, Flags flags)
: GPUTexture(width, height, layers, levels, samples, type, format), m_texture(texture) : GPUTexture(width, height, layers, levels, samples, type, format, flags), m_texture(texture)
{ {
} }
@ -1141,6 +1139,15 @@ void MetalTexture::MakeReadyForSampling()
dev.EndRenderPass(); dev.EndRenderPass();
} }
void MetalTexture::GenerateMipmaps()
{
DebugAssert(HasFlag(Flags::AllowGenerateMipmaps));
MetalDevice& dev = MetalDevice::GetInstance();
const bool is_inline = (m_use_fence_counter == dev.GetCurrentFenceCounter());
id<MTLBlitCommandEncoder> encoder = dev.GetBlitEncoder(is_inline);
[encoder generateMipmapsForTexture:m_texture];
}
void MetalTexture::SetDebugName(std::string_view name) void MetalTexture::SetDebugName(std::string_view name)
{ {
@autoreleasepool @autoreleasepool
@ -1151,14 +1158,18 @@ void MetalTexture::SetDebugName(std::string_view name)
std::unique_ptr<GPUTexture> MetalDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> MetalDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data, u32 data_stride) GPUTexture::Flags flags, const void* data, u32 data_stride,
Error* error)
{ {
if (!GPUTexture::ValidateConfig(width, height, layers, layers, samples, type, format)) if (!GPUTexture::ValidateConfig(width, height, layers, layers, samples, type, format, flags, error))
return {}; return {};
const MTLPixelFormat pixel_format = s_pixel_format_mapping[static_cast<u8>(format)]; const MTLPixelFormat pixel_format = s_pixel_format_mapping[static_cast<u8>(format)];
if (pixel_format == MTLPixelFormatInvalid) if (pixel_format == MTLPixelFormatInvalid)
{
Error::SetStringFmt(error, "Pixel format {} is not supported.", GPUTexture::GetFormatName(format));
return {}; return {};
}
@autoreleasepool @autoreleasepool
{ {
@ -1183,7 +1194,6 @@ std::unique_ptr<GPUTexture> MetalDevice::CreateTexture(u32 width, u32 height, u3
switch (type) switch (type)
{ {
case GPUTexture::Type::Texture: case GPUTexture::Type::Texture:
case GPUTexture::Type::DynamicTexture:
desc.usage = MTLTextureUsageShaderRead; desc.usage = MTLTextureUsageShaderRead;
break; break;
@ -1192,25 +1202,25 @@ std::unique_ptr<GPUTexture> MetalDevice::CreateTexture(u32 width, u32 height, u3
desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
break; break;
case GPUTexture::Type::RWTexture: DefaultCaseIsUnreachable();
desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; }
break;
default: if ((flags & (GPUTexture::Flags::AllowBindAsImage | GPUTexture::Flags::AllowMSAAResolveTarget)) !=
UnreachableCode(); GPUTexture::Flags::None)
break; {
desc.usage |= MTLTextureUsageShaderWrite;
} }
id<MTLTexture> tex = [m_device newTextureWithDescriptor:desc]; id<MTLTexture> tex = [m_device newTextureWithDescriptor:desc];
if (tex == nil) if (tex == nil)
{ {
ERROR_LOG("Failed to create {}x{} texture.", width, height); Error::SetStringView(error, "newTextureWithDescriptor() failed");
return {}; return {};
} }
// This one can *definitely* go on the upload buffer. // This one can *definitely* go on the upload buffer.
std::unique_ptr<GPUTexture> gtex( std::unique_ptr<GPUTexture> gtex(
new MetalTexture([tex retain], width, height, layers, levels, samples, type, format)); new MetalTexture([tex retain], width, height, layers, levels, samples, type, format, flags));
if (data) if (data)
{ {
// TODO: handle multi-level uploads... // TODO: handle multi-level uploads...
@ -1236,7 +1246,8 @@ MetalDownloadTexture::~MetalDownloadTexture()
} }
std::unique_ptr<MetalDownloadTexture> MetalDownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<MetalDownloadTexture> MetalDownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, u32 memory_stride) void* memory, size_t memory_size, u32 memory_stride,
Error* error)
{ {
@autoreleasepool @autoreleasepool
{ {
@ -1257,7 +1268,7 @@ std::unique_ptr<MetalDownloadTexture> MetalDownloadTexture::Create(u32 width, u3
buffer = [[dev.m_device newBufferWithLength:buffer_size options:options] retain]; buffer = [[dev.m_device newBufferWithLength:buffer_size options:options] retain];
if (buffer == nil) if (buffer == nil)
{ {
ERROR_LOG("Failed to create {} byte buffer", buffer_size); Error::SetStringFmt(error, "Failed to create {} byte buffer", buffer_size);
return {}; return {};
} }
@ -1282,7 +1293,7 @@ std::unique_ptr<MetalDownloadTexture> MetalDownloadTexture::Create(u32 width, u3
deallocator:nil] retain]; deallocator:nil] retain];
if (buffer == nil) if (buffer == nil)
{ {
ERROR_LOG("Failed to import {} byte buffer", page_aligned_size); Error::SetStringFmt(error, "Failed to import {} byte buffer", page_aligned_size);
return {}; return {};
} }
@ -1369,16 +1380,17 @@ void MetalDownloadTexture::SetDebugName(std::string_view name)
} }
} }
std::unique_ptr<GPUDownloadTexture> MetalDevice::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) std::unique_ptr<GPUDownloadTexture> MetalDevice::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
Error* error)
{ {
return MetalDownloadTexture::Create(width, height, format, nullptr, 0, 0); return MetalDownloadTexture::Create(width, height, format, nullptr, 0, 0, error);
} }
std::unique_ptr<GPUDownloadTexture> MetalDevice::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<GPUDownloadTexture> MetalDevice::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, void* memory, size_t memory_size,
u32 memory_stride) u32 memory_stride, Error* error)
{ {
return MetalDownloadTexture::Create(width, height, format, memory, memory_size, memory_stride); return MetalDownloadTexture::Create(width, height, format, memory, memory_size, memory_stride, error);
} }
MetalSampler::MetalSampler(id<MTLSamplerState> ss) : m_ss(ss) MetalSampler::MetalSampler(id<MTLSamplerState> ss) : m_ss(ss)
@ -1392,7 +1404,7 @@ void MetalSampler::SetDebugName(std::string_view name)
// lame.. have to put it on the descriptor :/ // lame.. have to put it on the descriptor :/
} }
std::unique_ptr<GPUSampler> MetalDevice::CreateSampler(const GPUSampler::Config& config) std::unique_ptr<GPUSampler> MetalDevice::CreateSampler(const GPUSampler::Config& config, Error* error)
{ {
@autoreleasepool @autoreleasepool
{ {
@ -1448,7 +1460,7 @@ std::unique_ptr<GPUSampler> MetalDevice::CreateSampler(const GPUSampler::Config&
} }
if (i == std::size(border_color_mapping)) if (i == std::size(border_color_mapping))
{ {
ERROR_LOG("Unsupported border color: {:08X}", config.border_color.GetValue()); Error::SetStringFmt(error, "Unsupported border color: {:08X}", config.border_color.GetValue());
return {}; return {};
} }
@ -1459,7 +1471,7 @@ std::unique_ptr<GPUSampler> MetalDevice::CreateSampler(const GPUSampler::Config&
id<MTLSamplerState> ss = [m_device newSamplerStateWithDescriptor:desc]; id<MTLSamplerState> ss = [m_device newSamplerStateWithDescriptor:desc];
if (ss == nil) if (ss == nil)
{ {
ERROR_LOG("Failed to create sampler state."); Error::SetStringView(error, "newSamplerStateWithDescriptor failed");
return {}; return {};
} }
@ -1550,6 +1562,7 @@ void MetalDevice::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u3
DebugAssert((dst_x + width) <= dst->GetMipWidth(dst_level)); DebugAssert((dst_x + width) <= dst->GetMipWidth(dst_level));
DebugAssert((dst_y + height) <= dst->GetMipHeight(dst_level)); DebugAssert((dst_y + height) <= dst->GetMipHeight(dst_level));
DebugAssert(!dst->IsMultisampled() && src->IsMultisampled()); DebugAssert(!dst->IsMultisampled() && src->IsMultisampled());
DebugAssert(dst->HasFlag(GPUTexture::Flags::AllowMSAAResolveTarget));
// Only does first level for now.. // Only does first level for now..
DebugAssert(dst_level == 0 && dst_layer == 0); DebugAssert(dst_level == 0 && dst_layer == 0);
@ -1767,9 +1780,9 @@ MetalTextureBuffer::~MetalTextureBuffer()
m_buffer.Destroy(); m_buffer.Destroy();
} }
bool MetalTextureBuffer::CreateBuffer(id<MTLDevice> device) bool MetalTextureBuffer::CreateBuffer(id<MTLDevice> device, Error* error)
{ {
return m_buffer.Create(device, GetSizeInBytes()); return m_buffer.Create(device, GetSizeInBytes(), error);
} }
void* MetalTextureBuffer::Map(u32 required_elements) void* MetalTextureBuffer::Map(u32 required_elements)
@ -1804,10 +1817,10 @@ void MetalTextureBuffer::SetDebugName(std::string_view name)
} }
std::unique_ptr<GPUTextureBuffer> MetalDevice::CreateTextureBuffer(GPUTextureBuffer::Format format, std::unique_ptr<GPUTextureBuffer> MetalDevice::CreateTextureBuffer(GPUTextureBuffer::Format format,
u32 size_in_elements) u32 size_in_elements, Error* error)
{ {
std::unique_ptr<MetalTextureBuffer> tb = std::make_unique<MetalTextureBuffer>(format, size_in_elements); std::unique_ptr<MetalTextureBuffer> tb = std::make_unique<MetalTextureBuffer>(format, size_in_elements);
if (!tb->CreateBuffer(m_device)) if (!tb->CreateBuffer(m_device, error))
tb.reset(); tb.reset();
return tb; return tb;

View File

@ -19,6 +19,8 @@
#include <deque> #include <deque>
#include <memory> #include <memory>
class Error;
class MetalStreamBuffer class MetalStreamBuffer
{ {
public: public:
@ -38,7 +40,7 @@ public:
ALWAYS_INLINE u32 GetCurrentSpace() const { return m_current_space; } ALWAYS_INLINE u32 GetCurrentSpace() const { return m_current_space; }
ALWAYS_INLINE u32 GetCurrentOffset() const { return m_current_offset; } ALWAYS_INLINE u32 GetCurrentOffset() const { return m_current_offset; }
bool Create(id<MTLDevice> device, u32 size); bool Create(id<MTLDevice> device, u32 size, Error* error);
void Destroy(); void Destroy();
bool ReserveMemory(u32 num_bytes, u32 alignment); bool ReserveMemory(u32 num_bytes, u32 alignment);

View File

@ -6,6 +6,7 @@
#include "common/align.h" #include "common/align.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/error.h"
#include "common/log.h" #include "common/log.h"
LOG_CHANNEL(GPUDevice); LOG_CHANNEL(GPUDevice);
@ -18,7 +19,7 @@ MetalStreamBuffer::~MetalStreamBuffer()
Destroy(); Destroy();
} }
bool MetalStreamBuffer::Create(id<MTLDevice> device, u32 size) bool MetalStreamBuffer::Create(id<MTLDevice> device, u32 size, Error* error)
{ {
@autoreleasepool @autoreleasepool
{ {
@ -27,7 +28,7 @@ bool MetalStreamBuffer::Create(id<MTLDevice> device, u32 size)
id<MTLBuffer> new_buffer = [device newBufferWithLength:size options:options]; id<MTLBuffer> new_buffer = [device newBufferWithLength:size options:options];
if (new_buffer == nil) if (new_buffer == nil)
{ {
ERROR_LOG("Failed to create buffer."); Error::SetStringView(error, "newBufferWithLength failed");
return false; return false;
} }

View File

@ -60,9 +60,10 @@ void OpenGLDevice::SetErrorObject(Error* errptr, std::string_view prefix, GLenum
std::unique_ptr<GPUTexture> OpenGLDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> OpenGLDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data, u32 data_stride) GPUTexture::Flags flags, const void* data /* = nullptr */,
u32 data_stride /* = 0 */, Error* error /* = nullptr */)
{ {
return OpenGLTexture::Create(width, height, layers, levels, samples, type, format, data, data_stride); return OpenGLTexture::Create(width, height, layers, levels, samples, type, format, flags, data, data_stride, error);
} }
bool OpenGLDevice::SupportsTextureFormat(GPUTexture::Format format) const bool OpenGLDevice::SupportsTextureFormat(GPUTexture::Format format) const

View File

@ -52,15 +52,18 @@ public:
std::optional<bool> exclusive_fullscreen_control, std::optional<bool> exclusive_fullscreen_control,
Error* error) override; Error* error) override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags,
const void* data = nullptr, u32 data_stride = 0) override; const void* data = nullptr, u32 data_stride = 0,
std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) override; Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements) override; std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config, Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements,
Error* error = nullptr) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, Error* error = nullptr) override;
u32 memory_stride) override; std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, u32 memory_stride,
Error* error = nullptr) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override; bool SupportsTextureFormat(GPUTexture::Format format) const override;
void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,

View File

@ -5,9 +5,9 @@
#include "common/align.h" #include "common/align.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/error.h"
#include <array> #include <array>
#include <cstdio>
OpenGLStreamBuffer::OpenGLStreamBuffer(GLenum target, GLuint buffer_id, u32 size) OpenGLStreamBuffer::OpenGLStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
: m_target(target), m_buffer_id(buffer_id), m_size(size) : m_target(target), m_buffer_id(buffer_id), m_size(size)
@ -65,7 +65,7 @@ public:
u32 GetChunkSize() const override { return m_size; } u32 GetChunkSize() const override { return m_size; }
static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size) static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size, Error* error)
{ {
glGetError(); glGetError();
@ -74,9 +74,10 @@ public:
glBindBuffer(target, buffer_id); glBindBuffer(target, buffer_id);
glBufferData(target, size, nullptr, GL_STREAM_DRAW); glBufferData(target, size, nullptr, GL_STREAM_DRAW);
GLenum err = glGetError(); const GLenum err = glGetError();
if (err != GL_NO_ERROR) if (err != GL_NO_ERROR) [[unlikely]]
{ {
Error::SetStringFmt(error, "Failed to create buffer: 0x{:X}", err);
glBindBuffer(target, 0); glBindBuffer(target, 0);
glDeleteBuffers(1, &buffer_id); glDeleteBuffers(1, &buffer_id);
return {}; return {};
@ -119,7 +120,7 @@ public:
u32 GetChunkSize() const override { return m_size; } u32 GetChunkSize() const override { return m_size; }
static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size) static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size, Error* error)
{ {
glGetError(); glGetError();
@ -128,9 +129,10 @@ public:
glBindBuffer(target, buffer_id); glBindBuffer(target, buffer_id);
glBufferData(target, size, nullptr, GL_STREAM_DRAW); glBufferData(target, size, nullptr, GL_STREAM_DRAW);
GLenum err = glGetError(); const GLenum err = glGetError();
if (err != GL_NO_ERROR) if (err != GL_NO_ERROR) [[unlikely]]
{ {
Error::SetStringFmt(error, "Failed to create buffer: 0x{:X}", err);
glBindBuffer(target, 0); glBindBuffer(target, 0);
glDeleteBuffers(1, &buffer_id); glDeleteBuffers(1, &buffer_id);
return {}; return {};
@ -283,7 +285,7 @@ public:
return prev_position; return prev_position;
} }
static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size, bool coherent = true) static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size, Error* error, bool coherent = true)
{ {
glGetError(); glGetError();
@ -298,9 +300,10 @@ public:
else if (GLAD_GL_EXT_buffer_storage) else if (GLAD_GL_EXT_buffer_storage)
glBufferStorageEXT(target, size, nullptr, flags); glBufferStorageEXT(target, size, nullptr, flags);
GLenum err = glGetError(); const GLenum err = glGetError();
if (err != GL_NO_ERROR) if (err != GL_NO_ERROR) [[unlikely]]
{ {
Error::SetStringFmt(error, "Failed to create buffer: 0x{:X}", err);
glBindBuffer(target, 0); glBindBuffer(target, 0);
glDeleteBuffers(1, &buffer_id); glDeleteBuffers(1, &buffer_id);
return {}; return {};
@ -325,12 +328,12 @@ private:
} // namespace } // namespace
std::unique_ptr<OpenGLStreamBuffer> OpenGLStreamBuffer::Create(GLenum target, u32 size) std::unique_ptr<OpenGLStreamBuffer> OpenGLStreamBuffer::Create(GLenum target, u32 size, Error* error /* = nullptr */)
{ {
std::unique_ptr<OpenGLStreamBuffer> buf; std::unique_ptr<OpenGLStreamBuffer> buf;
if (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage) if (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage)
{ {
buf = BufferStorageStreamBuffer::Create(target, size); buf = BufferStorageStreamBuffer::Create(target, size, error);
if (buf) if (buf)
return buf; return buf;
} }
@ -341,11 +344,11 @@ std::unique_ptr<OpenGLStreamBuffer> OpenGLStreamBuffer::Create(GLenum target, u3
if (std::strcmp(vendor, "ARM") == 0 || std::strcmp(vendor, "Qualcomm") == 0) if (std::strcmp(vendor, "ARM") == 0 || std::strcmp(vendor, "Qualcomm") == 0)
{ {
// Mali and Adreno drivers can't do sub-buffer tracking... // Mali and Adreno drivers can't do sub-buffer tracking...
return BufferDataStreamBuffer::Create(target, size); return BufferDataStreamBuffer::Create(target, size, error);
} }
return BufferSubDataStreamBuffer::Create(target, size); return BufferSubDataStreamBuffer::Create(target, size, error);
#else #else
return BufferDataStreamBuffer::Create(target, size); return BufferDataStreamBuffer::Create(target, size, error);
#endif #endif
} }

View File

@ -12,6 +12,8 @@
#include <tuple> #include <tuple>
#include <vector> #include <vector>
class Error;
class OpenGLStreamBuffer class OpenGLStreamBuffer
{ {
public: public:
@ -42,7 +44,7 @@ public:
/// Returns the minimum granularity of blocks which sync objects will be created around. /// Returns the minimum granularity of blocks which sync objects will be created around.
virtual u32 GetChunkSize() const = 0; virtual u32 GetChunkSize() const = 0;
static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size); static std::unique_ptr<OpenGLStreamBuffer> Create(GLenum target, u32 size, Error* error = nullptr);
protected: protected:
OpenGLStreamBuffer(GLenum target, GLuint buffer_id, u32 size); OpenGLStreamBuffer(GLenum target, GLuint buffer_id, u32 size);

View File

@ -7,6 +7,7 @@
#include "common/align.h" #include "common/align.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/error.h"
#include "common/intrin.h" #include "common/intrin.h"
#include "common/log.h" #include "common/log.h"
#include "common/string_util.h" #include "common/string_util.h"
@ -98,9 +99,9 @@ ALWAYS_INLINE static u32 GetUploadAlignment(u32 pitch)
} }
OpenGLTexture::OpenGLTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, OpenGLTexture::OpenGLTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
GLuint id) Flags flags, GLuint id)
: GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels), : GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
static_cast<u8>(samples), type, format), static_cast<u8>(samples), type, format, flags),
m_id(id) m_id(id)
{ {
} }
@ -126,14 +127,15 @@ bool OpenGLTexture::UseTextureStorage() const
} }
std::unique_ptr<OpenGLTexture> OpenGLTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<OpenGLTexture> OpenGLTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
Type type, Format format, const void* data, u32 data_pitch) Type type, Format format, Flags flags, const void* data,
u32 data_pitch, Error* error)
{ {
if (!ValidateConfig(width, height, layers, levels, samples, type, format)) if (!ValidateConfig(width, height, layers, levels, samples, type, format, flags, error))
return nullptr; return nullptr;
if (layers > 1 && data) if (layers > 1 && data)
{ {
ERROR_LOG("Loading texture array data not currently supported"); Error::SetStringView(error, "Loading texture array data not currently supported");
return nullptr; return nullptr;
} }
@ -235,15 +237,16 @@ std::unique_ptr<OpenGLTexture> OpenGLTexture::Create(u32 width, u32 height, u32
} }
} }
GLenum error = glGetError(); const GLenum gl_error = glGetError();
if (error != GL_NO_ERROR) if (gl_error != GL_NO_ERROR)
{ {
ERROR_LOG("Failed to create texture: 0x{:X}", error); Error::SetStringFmt(error, "Failed to create texture: 0x{:X}", gl_error);
glDeleteTextures(1, &id); glDeleteTextures(1, &id);
return nullptr; return nullptr;
} }
return std::unique_ptr<OpenGLTexture>(new OpenGLTexture(width, height, layers, levels, samples, type, format, id)); return std::unique_ptr<OpenGLTexture>(
new OpenGLTexture(width, height, layers, levels, samples, type, format, flags, id));
} }
void OpenGLTexture::CommitClear() void OpenGLTexture::CommitClear()
@ -372,6 +375,16 @@ void OpenGLTexture::Unmap()
sb->Unbind(); sb->Unbind();
} }
void OpenGLTexture::GenerateMipmaps()
{
DebugAssert(HasFlag(Flags::AllowGenerateMipmaps));
OpenGLDevice::BindUpdateTextureUnit();
const GLenum target = GetGLTarget();
glBindTexture(target, m_id);
glGenerateMipmap(target);
glBindTexture(target, 0);
}
void OpenGLTexture::SetDebugName(std::string_view name) void OpenGLTexture::SetDebugName(std::string_view name)
{ {
#ifdef _DEBUG #ifdef _DEBUG
@ -405,7 +418,7 @@ void OpenGLSampler::SetDebugName(std::string_view name)
#endif #endif
} }
std::unique_ptr<GPUSampler> OpenGLDevice::CreateSampler(const GPUSampler::Config& config) std::unique_ptr<GPUSampler> OpenGLDevice::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */)
{ {
static constexpr std::array<GLenum, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{ static constexpr std::array<GLenum, static_cast<u8>(GPUSampler::AddressMode::MaxCount)> ta = {{
GL_REPEAT, // Repeat GL_REPEAT, // Repeat
@ -433,7 +446,7 @@ std::unique_ptr<GPUSampler> OpenGLDevice::CreateSampler(const GPUSampler::Config
glGenSamplers(1, &sampler); glGenSamplers(1, &sampler);
if (glGetError() != GL_NO_ERROR) if (glGetError() != GL_NO_ERROR)
{ {
ERROR_LOG("Failed to create sampler: {:X}", sampler); Error::SetStringFmt(error, "Failed to create sampler: {:X}", sampler);
return {}; return {};
} }
@ -697,7 +710,7 @@ void OpenGLTextureBuffer::SetDebugName(std::string_view name)
} }
std::unique_ptr<GPUTextureBuffer> OpenGLDevice::CreateTextureBuffer(GPUTextureBuffer::Format format, std::unique_ptr<GPUTextureBuffer> OpenGLDevice::CreateTextureBuffer(GPUTextureBuffer::Format format,
u32 size_in_elements) u32 size_in_elements, Error* error)
{ {
const bool use_ssbo = OpenGLDevice::GetInstance().GetFeatures().texture_buffers_emulated_with_ssbo; const bool use_ssbo = OpenGLDevice::GetInstance().GetFeatures().texture_buffers_emulated_with_ssbo;
const u32 buffer_size = GPUTextureBuffer::GetElementSize(format) * size_in_elements; const u32 buffer_size = GPUTextureBuffer::GetElementSize(format) * size_in_elements;
@ -708,13 +721,13 @@ std::unique_ptr<GPUTextureBuffer> OpenGLDevice::CreateTextureBuffer(GPUTextureBu
glGetInteger64v(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo_size); glGetInteger64v(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo_size);
if (static_cast<GLint64>(buffer_size) > max_ssbo_size) if (static_cast<GLint64>(buffer_size) > max_ssbo_size)
{ {
ERROR_LOG("Buffer size of {} not supported, max is {}", buffer_size, max_ssbo_size); Error::SetStringFmt(error, "Buffer size of {} not supported, max is {}", buffer_size, max_ssbo_size);
return {}; return {};
} }
} }
const GLenum target = (use_ssbo ? GL_SHADER_STORAGE_BUFFER : GL_TEXTURE_BUFFER); const GLenum target = (use_ssbo ? GL_SHADER_STORAGE_BUFFER : GL_TEXTURE_BUFFER);
std::unique_ptr<OpenGLStreamBuffer> buffer = OpenGLStreamBuffer::Create(target, buffer_size); std::unique_ptr<OpenGLStreamBuffer> buffer = OpenGLStreamBuffer::Create(target, buffer_size, error);
if (!buffer) if (!buffer)
return {}; return {};
buffer->Unbind(); buffer->Unbind();
@ -726,7 +739,7 @@ std::unique_ptr<GPUTextureBuffer> OpenGLDevice::CreateTextureBuffer(GPUTextureBu
glGenTextures(1, &texture_id); glGenTextures(1, &texture_id);
if (const GLenum err = glGetError(); err != GL_NO_ERROR) if (const GLenum err = glGetError(); err != GL_NO_ERROR)
{ {
ERROR_LOG("Failed to create texture for buffer: 0x{:X}", err); Error::SetStringFmt(error, "Failed to create texture for buffer: 0x{:X}", err);
return {}; return {};
} }
@ -772,7 +785,8 @@ OpenGLDownloadTexture::~OpenGLDownloadTexture()
} }
std::unique_ptr<OpenGLDownloadTexture> OpenGLDownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<OpenGLDownloadTexture> OpenGLDownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, u32 memory_pitch) void* memory, size_t memory_size, u32 memory_pitch,
Error* error)
{ {
const u32 buffer_pitch = const u32 buffer_pitch =
memory ? memory_pitch : memory ? memory_pitch :
@ -801,7 +815,7 @@ std::unique_ptr<OpenGLDownloadTexture> OpenGLDownloadTexture::Create(u32 width,
if (!buffer_map) if (!buffer_map)
{ {
ERROR_LOG("Failed to map persistent download buffer"); Error::SetStringView(error, "Failed to map persistent download buffer");
glDeleteBuffers(1, &buffer_id); glDeleteBuffers(1, &buffer_id);
return {}; return {};
} }
@ -814,8 +828,11 @@ std::unique_ptr<OpenGLDownloadTexture> OpenGLDownloadTexture::Create(u32 width,
const bool imported = (memory != nullptr); const bool imported = (memory != nullptr);
u8* cpu_buffer = u8* cpu_buffer =
imported ? static_cast<u8*>(memory) : static_cast<u8*>(Common::AlignedMalloc(buffer_size, VECTOR_ALIGNMENT)); imported ? static_cast<u8*>(memory) : static_cast<u8*>(Common::AlignedMalloc(buffer_size, VECTOR_ALIGNMENT));
if (!cpu_buffer) if (!cpu_buffer) [[unlikely]]
{
Error::SetStringView(error, "Failed to get client-side memory pointer.");
return {}; return {};
}
return std::unique_ptr<OpenGLDownloadTexture>( return std::unique_ptr<OpenGLDownloadTexture>(
new OpenGLDownloadTexture(width, height, format, imported, 0, cpu_buffer, buffer_size, cpu_buffer, buffer_pitch)); new OpenGLDownloadTexture(width, height, format, imported, 0, cpu_buffer, buffer_size, cpu_buffer, buffer_pitch));
@ -929,16 +946,17 @@ void OpenGLDownloadTexture::SetDebugName(std::string_view name)
glObjectLabel(GL_BUFFER, m_buffer_id, static_cast<GLsizei>(name.length()), name.data()); glObjectLabel(GL_BUFFER, m_buffer_id, static_cast<GLsizei>(name.length()), name.data());
} }
std::unique_ptr<GPUDownloadTexture> OpenGLDevice::CreateDownloadTexture(u32 width, u32 height, std::unique_ptr<GPUDownloadTexture>
GPUTexture::Format format) OpenGLDevice::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, Error* error /* = nullptr */)
{ {
return OpenGLDownloadTexture::Create(width, height, format, nullptr, 0, 0); return OpenGLDownloadTexture::Create(width, height, format, nullptr, 0, 0, error);
} }
std::unique_ptr<GPUDownloadTexture> OpenGLDevice::CreateDownloadTexture(u32 width, u32 height, std::unique_ptr<GPUDownloadTexture> OpenGLDevice::CreateDownloadTexture(u32 width, u32 height,
GPUTexture::Format format, void* memory, GPUTexture::Format format, void* memory,
size_t memory_size, u32 memory_stride) size_t memory_size, u32 memory_stride,
Error* error /* = nullptr */)
{ {
// not _really_ memory importing, but PBOs are broken on Intel.... // not _really_ memory importing, but PBOs are broken on Intel....
return OpenGLDownloadTexture::Create(width, height, format, memory, memory_size, memory_stride); return OpenGLDownloadTexture::Create(width, height, format, memory, memory_size, memory_stride, error);
} }

View File

@ -28,11 +28,13 @@ public:
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
void Unmap() override; void Unmap() override;
void GenerateMipmaps() override;
void SetDebugName(std::string_view name) override; void SetDebugName(std::string_view name) override;
static std::unique_ptr<OpenGLTexture> Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, static std::unique_ptr<OpenGLTexture> Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type,
Format format, const void* data = nullptr, u32 data_pitch = 0); Format format, Flags flags, const void* data, u32 data_pitch,
Error* error);
bool UseTextureStorage() const; bool UseTextureStorage() const;
@ -46,7 +48,8 @@ public:
OpenGLTexture& operator=(const OpenGLTexture&) = delete; OpenGLTexture& operator=(const OpenGLTexture&) = delete;
private: private:
OpenGLTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, GLuint id); OpenGLTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, Flags flags,
GLuint id);
GLuint m_id = 0; GLuint m_id = 0;
@ -108,7 +111,7 @@ public:
~OpenGLDownloadTexture() override; ~OpenGLDownloadTexture() override;
static std::unique_ptr<OpenGLDownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format, void* memory, static std::unique_ptr<OpenGLDownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format, void* memory,
size_t memory_size, u32 memory_pitch); size_t memory_size, u32 memory_pitch, Error* error);
void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height, void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height,
u32 src_layer, u32 src_level, bool use_transfer_pitch) override; u32 src_layer, u32 src_level, bool use_transfer_pitch) override;

View File

@ -564,10 +564,12 @@ bool PostProcessing::Chain::CheckTargets(GPUTexture::Format target_format, u32 t
// In case any allocs fail. // In case any allocs fail.
DestroyTextures(); DestroyTextures();
if (!(m_input_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1, if (!(m_input_texture =
GPUTexture::Type::RenderTarget, target_format)) || g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1, GPUTexture::Type::RenderTarget,
!(m_output_texture = g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1, target_format, GPUTexture::Flags::None)) ||
GPUTexture::Type::RenderTarget, target_format))) !(m_output_texture =
g_gpu_device->FetchTexture(target_width, target_height, 1, 1, 1, GPUTexture::Type::RenderTarget,
target_format, GPUTexture::Flags::None)))
{ {
DestroyTextures(); DestroyTextures();
return false; return false;
@ -806,7 +808,7 @@ GPUTexture* PostProcessing::GetDummyTexture()
const u32 zero = 0; const u32 zero = 0;
s_dummy_texture = g_gpu_device->FetchTexture(1, 1, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8, s_dummy_texture = g_gpu_device->FetchTexture(1, 1, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8,
&zero, sizeof(zero)); GPUTexture::Flags::None, &zero, sizeof(zero));
if (!s_dummy_texture) if (!s_dummy_texture)
ERROR_LOG("Failed to create dummy texture."); ERROR_LOG("Failed to create dummy texture.");

View File

@ -1138,12 +1138,10 @@ bool PostProcessing::ReShadeFXShader::CreatePasses(GPUTexture::Format backbuffer
tex.rt_scale = 0.0f; tex.rt_scale = 0.0f;
tex.texture = g_gpu_device->FetchTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture, tex.texture = g_gpu_device->FetchTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
GPUTexture::Format::RGBA8, image.GetPixels(), image.GetPitch()); GPUTexture::Format::RGBA8, GPUTexture::Flags::None, image.GetPixels(),
image.GetPitch(), error);
if (!tex.texture) if (!tex.texture)
{
Error::SetStringFmt(error, "Failed to create {}x{} texture ({})", image.GetWidth(), image.GetHeight(), source);
return false; return false;
}
DEV_LOG("Loaded {}x{} texture ({})", image.GetWidth(), image.GetHeight(), source); DEV_LOG("Loaded {}x{} texture ({})", image.GetWidth(), image.GetHeight(), source);
} }
@ -1457,12 +1455,10 @@ bool PostProcessing::ReShadeFXShader::ResizeOutput(GPUTexture::Format format, u3
const u32 t_width = std::max(static_cast<u32>(static_cast<float>(width) * tex.rt_scale), 1u); const u32 t_width = std::max(static_cast<u32>(static_cast<float>(width) * tex.rt_scale), 1u);
const u32 t_height = std::max(static_cast<u32>(static_cast<float>(height) * tex.rt_scale), 1u); const u32 t_height = std::max(static_cast<u32>(static_cast<float>(height) * tex.rt_scale), 1u);
tex.texture = g_gpu_device->FetchTexture(t_width, t_height, 1, 1, 1, GPUTexture::Type::RenderTarget, tex.format); tex.texture = g_gpu_device->FetchTexture(t_width, t_height, 1, 1, 1, GPUTexture::Type::RenderTarget, tex.format,
GPUTexture::Flags::None);
if (!tex.texture) if (!tex.texture)
{
ERROR_LOG("Failed to create {}x{} texture", t_width, t_height);
return {}; return {};
}
} }
m_valid = true; m_valid = true;

View File

@ -2012,11 +2012,8 @@ bool VulkanDevice::CreateDeviceAndMainSwapChain(std::string_view adapter, Featur
m_main_swap_chain = std::move(swap_chain); m_main_swap_chain = std::move(swap_chain);
} }
if (!CreateNullTexture()) if (!CreateNullTexture(error))
{
Error::SetStringView(error, "Failed to create dummy texture");
return false; return false;
}
if (!CreateBuffers() || !CreatePersistentDescriptorSets()) if (!CreateBuffers() || !CreatePersistentDescriptorSets())
{ {
@ -2762,12 +2759,15 @@ void VulkanDevice::UnmapUniformBuffer(u32 size)
m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS; m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS;
} }
bool VulkanDevice::CreateNullTexture() bool VulkanDevice::CreateNullTexture(Error* error)
{ {
m_null_texture = VulkanTexture::Create(1, 1, 1, 1, 1, GPUTexture::Type::RWTexture, GPUTexture::Format::RGBA8, m_null_texture = VulkanTexture::Create(1, 1, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8,
VK_FORMAT_R8G8B8A8_UNORM); GPUTexture::Flags::AllowBindAsImage, VK_FORMAT_R8G8B8A8_UNORM, error);
if (!m_null_texture) if (!m_null_texture)
{
Error::AddPrefix(error, "Failed to create null texture: ");
return false; return false;
}
const VkCommandBuffer cmdbuf = GetCurrentCommandBuffer(); const VkCommandBuffer cmdbuf = GetCurrentCommandBuffer();
const VkImageSubresourceRange srr{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u}; const VkImageSubresourceRange srr{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
@ -2779,9 +2779,12 @@ bool VulkanDevice::CreateNullTexture()
Vulkan::SetObjectName(m_device, m_null_texture->GetView(), "Null texture view"); Vulkan::SetObjectName(m_device, m_null_texture->GetView(), "Null texture view");
// Bind null texture and point sampler state to all. // Bind null texture and point sampler state to all.
const VkSampler point_sampler = GetSampler(GPUSampler::GetNearestConfig()); const VkSampler point_sampler = GetSampler(GPUSampler::GetNearestConfig(), error);
if (point_sampler == VK_NULL_HANDLE) if (point_sampler == VK_NULL_HANDLE)
{
Error::AddPrefix(error, "Failed to get nearest sampler for init bind: ");
return false; return false;
}
for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++) for (u32 i = 0; i < MAX_TEXTURE_SAMPLERS; i++)
m_current_samplers[i] = point_sampler; m_current_samplers[i] = point_sampler;
@ -3010,10 +3013,14 @@ void VulkanDevice::RenderBlankFrame(VulkanSwapChain* swap_chain)
} }
bool VulkanDevice::TryImportHostMemory(void* data, size_t data_size, VkBufferUsageFlags buffer_usage, bool VulkanDevice::TryImportHostMemory(void* data, size_t data_size, VkBufferUsageFlags buffer_usage,
VkDeviceMemory* out_memory, VkBuffer* out_buffer, VkDeviceSize* out_offset) VkDeviceMemory* out_memory, VkBuffer* out_buffer, VkDeviceSize* out_offset,
Error* error)
{ {
if (!m_optional_extensions.vk_ext_external_memory_host) if (!m_optional_extensions.vk_ext_external_memory_host)
{
Error::SetStringView(error, "VK_EXT_external_memory_host is not supported.");
return false; return false;
}
// Align to the nearest page // Align to the nearest page
void* data_aligned = void* data_aligned =
@ -3031,7 +3038,7 @@ bool VulkanDevice::TryImportHostMemory(void* data, size_t data_size, VkBufferUsa
data_aligned, &pointer_properties); data_aligned, &pointer_properties);
if (res != VK_SUCCESS || pointer_properties.memoryTypeBits == 0) if (res != VK_SUCCESS || pointer_properties.memoryTypeBits == 0)
{ {
LOG_VULKAN_ERROR(res, "vkGetMemoryHostPointerPropertiesEXT() failed: "); Vulkan::SetErrorObject(error, "vkGetMemoryHostPointerPropertiesEXT() failed: ", res);
return false; return false;
} }
@ -3044,7 +3051,7 @@ bool VulkanDevice::TryImportHostMemory(void* data, size_t data_size, VkBufferUsa
res = vmaFindMemoryTypeIndex(m_allocator, pointer_properties.memoryTypeBits, &vma_alloc_info, &memory_index); res = vmaFindMemoryTypeIndex(m_allocator, pointer_properties.memoryTypeBits, &vma_alloc_info, &memory_index);
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
{ {
LOG_VULKAN_ERROR(res, "vmaFindMemoryTypeIndex() failed: "); Vulkan::SetErrorObject(error, "vmaFindMemoryTypeIndex() failed: ", res);
return false; return false;
} }
@ -3060,7 +3067,7 @@ bool VulkanDevice::TryImportHostMemory(void* data, size_t data_size, VkBufferUsa
res = vkAllocateMemory(m_device, &alloc_info, nullptr, &imported_memory); res = vkAllocateMemory(m_device, &alloc_info, nullptr, &imported_memory);
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
{ {
LOG_VULKAN_ERROR(res, "vkAllocateMemory() failed: "); Vulkan::SetErrorObject(error, "vkAllocateMemory() failed: ", res);
return false; return false;
} }
@ -3080,7 +3087,7 @@ bool VulkanDevice::TryImportHostMemory(void* data, size_t data_size, VkBufferUsa
res = vkCreateBuffer(m_device, &buffer_info, nullptr, &imported_buffer); res = vkCreateBuffer(m_device, &buffer_info, nullptr, &imported_buffer);
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
{ {
LOG_VULKAN_ERROR(res, "vkCreateBuffer() failed: "); Vulkan::SetErrorObject(error, "vkCreateBuffer() failed: ", res);
if (imported_memory != VK_NULL_HANDLE) if (imported_memory != VK_NULL_HANDLE)
vkFreeMemory(m_device, imported_memory, nullptr); vkFreeMemory(m_device, imported_memory, nullptr);
@ -3125,14 +3132,13 @@ void VulkanDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUText
if (InRenderPass()) if (InRenderPass())
EndRenderPass(); EndRenderPass();
m_current_framebuffer = VK_NULL_HANDLE;
if (m_num_current_render_targets == 0 && !m_current_depth_target) if (m_num_current_render_targets == 0 && !m_current_depth_target)
{
m_current_framebuffer = VK_NULL_HANDLE;
return; return;
}
if (!m_optional_extensions.vk_khr_dynamic_rendering || if (!(flags & GPUPipeline::BindRenderTargetsAsImages) &&
((flags & GPUPipeline::ColorFeedbackLoop) && !m_optional_extensions.vk_khr_dynamic_rendering_local_read)) (!m_optional_extensions.vk_khr_dynamic_rendering ||
((flags & GPUPipeline::ColorFeedbackLoop) && !m_optional_extensions.vk_khr_dynamic_rendering_local_read)))
{ {
m_current_framebuffer = m_framebuffer_manager.Lookup( m_current_framebuffer = m_framebuffer_manager.Lookup(
(m_num_current_render_targets > 0) ? reinterpret_cast<GPUTexture**>(m_current_render_targets.data()) : nullptr, (m_num_current_render_targets > 0) ? reinterpret_cast<GPUTexture**>(m_current_render_targets.data()) : nullptr,
@ -3594,7 +3600,7 @@ void VulkanDevice::UnbindTexture(VulkanTexture* tex)
} }
} }
if (tex->IsRenderTarget() || tex->IsRWTexture()) if (tex->IsRenderTarget())
{ {
for (u32 i = 0; i < m_num_current_render_targets; i++) for (u32 i = 0; i < m_num_current_render_targets; i++)
{ {

View File

@ -88,15 +88,18 @@ public:
std::optional<bool> exclusive_fullscreen_control, std::optional<bool> exclusive_fullscreen_control,
Error* error) override; Error* error) override;
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags,
const void* data = nullptr, u32 data_stride = 0) override; const void* data = nullptr, u32 data_stride = 0,
std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) override; Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements) override; std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config, Error* error = nullptr) override;
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements,
Error* error = nullptr) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) override;
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, Error* error = nullptr) override;
u32 memory_stride) override; std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, u32 memory_stride,
Error* error = nullptr) override;
bool SupportsTextureFormat(GPUTexture::Format format) const override; bool SupportsTextureFormat(GPUTexture::Format format) const override;
void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src, void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
@ -351,20 +354,20 @@ private:
void DestroyCommandBuffers(); void DestroyCommandBuffers();
bool CreatePersistentDescriptorPool(); bool CreatePersistentDescriptorPool();
void DestroyPersistentDescriptorPool(); void DestroyPersistentDescriptorPool();
bool CreateNullTexture(); bool CreateNullTexture(Error* error);
bool CreateBuffers(); bool CreateBuffers();
void DestroyBuffers(); void DestroyBuffers();
bool CreatePipelineLayouts(); bool CreatePipelineLayouts();
void DestroyPipelineLayouts(); void DestroyPipelineLayouts();
bool CreatePersistentDescriptorSets(); bool CreatePersistentDescriptorSets();
void DestroyPersistentDescriptorSets(); void DestroyPersistentDescriptorSets();
VkSampler GetSampler(const GPUSampler::Config& config); VkSampler GetSampler(const GPUSampler::Config& config, Error* error = nullptr);
void DestroySamplers(); void DestroySamplers();
void RenderBlankFrame(VulkanSwapChain* swap_chain); void RenderBlankFrame(VulkanSwapChain* swap_chain);
bool TryImportHostMemory(void* data, size_t data_size, VkBufferUsageFlags buffer_usage, VkDeviceMemory* out_memory, bool TryImportHostMemory(void* data, size_t data_size, VkBufferUsageFlags buffer_usage, VkDeviceMemory* out_memory,
VkBuffer* out_buffer, VkDeviceSize* out_offset); VkBuffer* out_buffer, VkDeviceSize* out_offset, Error* error);
/// Set dirty flags on everything to force re-bind at next draw time. /// Set dirty flags on everything to force re-bind at next draw time.
void InvalidateCachedState(); void InvalidateCachedState();

View File

@ -8,6 +8,7 @@
#include "common/align.h" #include "common/align.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/bitutils.h" #include "common/bitutils.h"
#include "common/error.h"
#include "common/log.h" #include "common/log.h"
LOG_CHANNEL(GPUDevice); LOG_CHANNEL(GPUDevice);
@ -42,9 +43,9 @@ static VkImageLayout GetVkImageLayout(VulkanTexture::Layout layout)
} }
VulkanTexture::VulkanTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, VulkanTexture::VulkanTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
VkImage image, VmaAllocation allocation, VkImageView view, VkFormat vk_format) Flags flags, VkImage image, VmaAllocation allocation, VkImageView view, VkFormat vk_format)
: GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels), : GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
static_cast<u8>(samples), type, format), static_cast<u8>(samples), type, format, flags),
m_image(image), m_allocation(allocation), m_view(view), m_vk_format(vk_format) m_image(image), m_allocation(allocation), m_view(view), m_vk_format(vk_format)
{ {
} }
@ -55,9 +56,10 @@ VulkanTexture::~VulkanTexture()
} }
std::unique_ptr<VulkanTexture> VulkanTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<VulkanTexture> VulkanTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
Type type, Format format, VkFormat vk_format) Type type, Format format, Flags flags, VkFormat vk_format,
Error* error)
{ {
if (!ValidateConfig(width, height, layers, levels, samples, type, format)) if (!ValidateConfig(width, height, layers, levels, samples, type, format, flags, error))
return {}; return {};
VulkanDevice& dev = VulkanDevice::GetInstance(); VulkanDevice& dev = VulkanDevice::GetInstance();
@ -92,11 +94,9 @@ std::unique_ptr<VulkanTexture> VulkanTexture::Create(u32 width, u32 height, u32
s_identity_swizzle, s_identity_swizzle,
{VK_IMAGE_ASPECT_COLOR_BIT, 0, static_cast<u32>(levels), 0, 1}}; {VK_IMAGE_ASPECT_COLOR_BIT, 0, static_cast<u32>(levels), 0, 1}};
// TODO: Don't need the feedback loop stuff yet.
switch (type) switch (type)
{ {
case Type::Texture: case Type::Texture:
case Type::DynamicTexture:
{ {
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
} }
@ -120,17 +120,13 @@ std::unique_ptr<VulkanTexture> VulkanTexture::Create(u32 width, u32 height, u32
} }
break; break;
case Type::RWTexture: DefaultCaseIsUnreachable();
{ }
DebugAssert(levels == 1);
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
break;
default: if ((flags & Flags::AllowBindAsImage) != Flags::None)
return {}; {
DebugAssert(levels == 1);
ici.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
} }
// Use dedicated allocations for typical RT size // Use dedicated allocations for typical RT size
@ -146,14 +142,9 @@ std::unique_ptr<VulkanTexture> VulkanTexture::Create(u32 width, u32 height, u32
aci.flags &= ~VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; aci.flags &= ~VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
res = vmaCreateImage(dev.GetAllocator(), &ici, &aci, &image, &allocation, nullptr); res = vmaCreateImage(dev.GetAllocator(), &ici, &aci, &image, &allocation, nullptr);
} }
if (res == VK_ERROR_OUT_OF_DEVICE_MEMORY) if (res != VK_SUCCESS)
{ {
ERROR_LOG("Failed to allocate device memory for {}x{} texture", width, height); Vulkan::SetErrorObject(error, "vmaCreateImage failed: ", res);
return {};
}
else if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vmaCreateImage failed: ");
return {}; return {};
} }
@ -162,13 +153,13 @@ std::unique_ptr<VulkanTexture> VulkanTexture::Create(u32 width, u32 height, u32
res = vkCreateImageView(dev.GetVulkanDevice(), &vci, nullptr, &view); res = vkCreateImageView(dev.GetVulkanDevice(), &vci, nullptr, &view);
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
{ {
LOG_VULKAN_ERROR(res, "vkCreateImageView failed: "); Vulkan::SetErrorObject(error, "vkCreateImageView failed: ", res);
vmaDestroyImage(dev.GetAllocator(), image, allocation); vmaDestroyImage(dev.GetAllocator(), image, allocation);
return {}; return {};
} }
return std::unique_ptr<VulkanTexture>( return std::unique_ptr<VulkanTexture>(
new VulkanTexture(width, height, layers, levels, samples, type, format, image, allocation, view, vk_format)); new VulkanTexture(width, height, layers, levels, samples, type, format, flags, image, allocation, view, vk_format));
} }
void VulkanTexture::Destroy(bool defer) void VulkanTexture::Destroy(bool defer)
@ -228,10 +219,9 @@ VkClearDepthStencilValue VulkanTexture::GetClearDepthValue() const
VkCommandBuffer VulkanTexture::GetCommandBufferForUpdate() VkCommandBuffer VulkanTexture::GetCommandBufferForUpdate()
{ {
VulkanDevice& dev = VulkanDevice::GetInstance(); VulkanDevice& dev = VulkanDevice::GetInstance();
if ((m_type != Type::Texture && m_type != Type::DynamicTexture) || if (m_type != Type::Texture || m_use_fence_counter == dev.GetCurrentFenceCounter())
m_use_fence_counter == dev.GetCurrentFenceCounter())
{ {
// Console.WriteLn("Texture update within frame, can't use do beforehand"); // DEV_LOG("Texture update within frame, can't use do beforehand");
if (dev.InRenderPass()) if (dev.InRenderPass())
dev.EndRenderPass(); dev.EndRenderPass();
return dev.GetCurrentCommandBuffer(); return dev.GetCurrentCommandBuffer();
@ -730,13 +720,52 @@ void VulkanTexture::MakeReadyForSampling()
TransitionToLayout(Layout::ShaderReadOnly); TransitionToLayout(Layout::ShaderReadOnly);
} }
void VulkanTexture::GenerateMipmaps()
{
DebugAssert(HasFlag(Flags::AllowGenerateMipmaps));
const VkCommandBuffer cmdbuf = GetCommandBufferForUpdate();
if (m_layout == Layout::Undefined)
TransitionToLayout(cmdbuf, Layout::TransferSrc);
for (u32 layer = 0; layer < m_layers; layer++)
{
for (u32 dst_level = 1; dst_level < m_levels; dst_level++)
{
const u32 src_level = dst_level - 1;
const u32 src_width = std::max<u32>(m_width >> src_level, 1u);
const u32 src_height = std::max<u32>(m_height >> src_level, 1u);
const u32 dst_width = std::max<u32>(m_width >> dst_level, 1u);
const u32 dst_height = std::max<u32>(m_height >> dst_level, 1u);
TransitionSubresourcesToLayout(cmdbuf, layer, 1, src_level, 1, m_layout, Layout::TransferSrc);
TransitionSubresourcesToLayout(cmdbuf, layer, 1, dst_level, 1, m_layout, Layout::TransferDst);
const VkImageBlit blit = {
{VK_IMAGE_ASPECT_COLOR_BIT, src_level, 0u, 1u}, // srcSubresource
{{0, 0, 0}, {static_cast<s32>(src_width), static_cast<s32>(src_height), 1}}, // srcOffsets
{VK_IMAGE_ASPECT_COLOR_BIT, dst_level, 0u, 1u}, // dstSubresource
{{0, 0, 0}, {static_cast<s32>(dst_width), static_cast<s32>(dst_height), 1}} // dstOffsets
};
vkCmdBlitImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
TransitionSubresourcesToLayout(cmdbuf, layer, 1, src_level, 1, Layout::TransferSrc, m_layout);
TransitionSubresourcesToLayout(cmdbuf, layer, 1, dst_level, 1, Layout::TransferDst, m_layout);
}
}
}
std::unique_ptr<GPUTexture> VulkanDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> VulkanDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data /* = nullptr */, u32 data_stride /* = 0 */) GPUTexture::Flags flags, const void* data /* = nullptr */,
u32 data_stride /* = 0 */, Error* error /* = nullptr */)
{ {
const VkFormat vk_format = VulkanDevice::TEXTURE_FORMAT_MAPPING[static_cast<u8>(format)]; const VkFormat vk_format = VulkanDevice::TEXTURE_FORMAT_MAPPING[static_cast<u8>(format)];
std::unique_ptr<VulkanTexture> tex = std::unique_ptr<VulkanTexture> tex =
VulkanTexture::Create(width, height, layers, levels, samples, type, format, vk_format); VulkanTexture::Create(width, height, layers, levels, samples, type, format, flags, vk_format, error);
if (tex && data) if (tex && data)
tex->Update(0, 0, width, height, data, data_stride); tex->Update(0, 0, width, height, data, data_stride);
@ -757,7 +786,7 @@ void VulkanSampler::SetDebugName(std::string_view name)
Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_sampler, name); Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_sampler, name);
} }
VkSampler VulkanDevice::GetSampler(const GPUSampler::Config& config) VkSampler VulkanDevice::GetSampler(const GPUSampler::Config& config, Error* error)
{ {
const auto it = m_sampler_map.find(config.key); const auto it = m_sampler_map.find(config.key);
if (it != m_sampler_map.end()) if (it != m_sampler_map.end())
@ -833,7 +862,10 @@ VkSampler VulkanDevice::GetSampler(const GPUSampler::Config& config)
VkSampler sampler = VK_NULL_HANDLE; VkSampler sampler = VK_NULL_HANDLE;
VkResult res = vkCreateSampler(m_device, &ci, nullptr, &sampler); VkResult res = vkCreateSampler(m_device, &ci, nullptr, &sampler);
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateSampler() failed: "); LOG_VULKAN_ERROR(res, "vkCreateSampler() failed: ");
Vulkan::SetErrorObject(error, "vkCreateSampler() failed: ", res);
}
m_sampler_map.emplace(config.key, sampler); m_sampler_map.emplace(config.key, sampler);
return sampler; return sampler;
@ -849,9 +881,9 @@ void VulkanDevice::DestroySamplers()
m_sampler_map.clear(); m_sampler_map.clear();
} }
std::unique_ptr<GPUSampler> VulkanDevice::CreateSampler(const GPUSampler::Config& config) std::unique_ptr<GPUSampler> VulkanDevice::CreateSampler(const GPUSampler::Config& config, Error* error /* = nullptr */)
{ {
const VkSampler vsampler = GetSampler(config); const VkSampler vsampler = GetSampler(config, error);
if (vsampler == VK_NULL_HANDLE) if (vsampler == VK_NULL_HANDLE)
return {}; return {};
@ -925,7 +957,7 @@ void VulkanTextureBuffer::SetDebugName(std::string_view name)
} }
std::unique_ptr<GPUTextureBuffer> VulkanDevice::CreateTextureBuffer(GPUTextureBuffer::Format format, std::unique_ptr<GPUTextureBuffer> VulkanDevice::CreateTextureBuffer(GPUTextureBuffer::Format format,
u32 size_in_elements) u32 size_in_elements, Error* error)
{ {
static constexpr std::array<VkFormat, static_cast<u8>(GPUTextureBuffer::Format::MaxCount)> format_mapping = {{ static constexpr std::array<VkFormat, static_cast<u8>(GPUTextureBuffer::Format::MaxCount)> format_mapping = {{
VK_FORMAT_R16_UINT, // R16UI VK_FORMAT_R16_UINT, // R16UI
@ -939,7 +971,7 @@ std::unique_ptr<GPUTextureBuffer> VulkanDevice::CreateTextureBuffer(GPUTextureBu
tb->m_descriptor_set = AllocatePersistentDescriptorSet(m_single_texture_buffer_ds_layout); tb->m_descriptor_set = AllocatePersistentDescriptorSet(m_single_texture_buffer_ds_layout);
if (tb->m_descriptor_set == VK_NULL_HANDLE) if (tb->m_descriptor_set == VK_NULL_HANDLE)
{ {
ERROR_LOG("Failed to allocate persistent descriptor set for texture buffer."); Error::SetStringView(error, "Failed to allocate persistent descriptor set for texture buffer.");
tb->Destroy(false); tb->Destroy(false);
return {}; return {};
} }
@ -996,7 +1028,7 @@ VulkanDownloadTexture::~VulkanDownloadTexture()
std::unique_ptr<VulkanDownloadTexture> VulkanDownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format, std::unique_ptr<VulkanDownloadTexture> VulkanDownloadTexture::Create(u32 width, u32 height, GPUTexture::Format format,
void* memory, size_t memory_size, void* memory, size_t memory_size,
u32 memory_stride) u32 memory_stride, Error* error)
{ {
VulkanDevice& dev = VulkanDevice::GetInstance(); VulkanDevice& dev = VulkanDevice::GetInstance();
VmaAllocation allocation = VK_NULL_HANDLE; VmaAllocation allocation = VK_NULL_HANDLE;
@ -1031,7 +1063,7 @@ std::unique_ptr<VulkanDownloadTexture> VulkanDownloadTexture::Create(u32 width,
VkResult res = vmaCreateBuffer(VulkanDevice::GetInstance().GetAllocator(), &bci, &aci, &buffer, &allocation, &ai); VkResult res = vmaCreateBuffer(VulkanDevice::GetInstance().GetAllocator(), &bci, &aci, &buffer, &allocation, &ai);
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
{ {
LOG_VULKAN_ERROR(res, "vmaCreateBuffer() failed: "); Vulkan::SetErrorObject(error, "vmaCreateBuffer() failed: ", res);
return {}; return {};
} }
@ -1045,7 +1077,7 @@ std::unique_ptr<VulkanDownloadTexture> VulkanDownloadTexture::Create(u32 width,
Assert(buffer_size <= memory_size); Assert(buffer_size <= memory_size);
if (!dev.TryImportHostMemory(memory, memory_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, &dev_memory, &buffer, if (!dev.TryImportHostMemory(memory, memory_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, &dev_memory, &buffer,
&memory_offset)) &memory_offset, error))
{ {
return {}; return {};
} }
@ -1177,15 +1209,16 @@ void VulkanDownloadTexture::SetDebugName(std::string_view name)
Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_buffer, name); Vulkan::SetObjectName(VulkanDevice::GetInstance().GetVulkanDevice(), m_buffer, name);
} }
std::unique_ptr<GPUDownloadTexture> VulkanDevice::CreateDownloadTexture(u32 width, u32 height, std::unique_ptr<GPUDownloadTexture>
GPUTexture::Format format) VulkanDevice::CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format, Error* error /* = nullptr */)
{ {
return VulkanDownloadTexture::Create(width, height, format, nullptr, 0, 0); return VulkanDownloadTexture::Create(width, height, format, nullptr, 0, 0, error);
} }
std::unique_ptr<GPUDownloadTexture> VulkanDevice::CreateDownloadTexture(u32 width, u32 height, std::unique_ptr<GPUDownloadTexture> VulkanDevice::CreateDownloadTexture(u32 width, u32 height,
GPUTexture::Format format, void* memory, GPUTexture::Format format, void* memory,
size_t memory_size, u32 memory_stride) size_t memory_size, u32 memory_stride,
Error* error /* = nullptr */)
{ {
return VulkanDownloadTexture::Create(width, height, format, memory, memory_size, memory_stride); return VulkanDownloadTexture::Create(width, height, format, memory, memory_size, memory_stride, error);
} }

View File

@ -38,7 +38,7 @@ public:
~VulkanTexture() override; ~VulkanTexture() override;
static std::unique_ptr<VulkanTexture> Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, static std::unique_ptr<VulkanTexture> Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type,
Format format, VkFormat vk_format); Format format, Flags flags, VkFormat vk_format, Error* error);
void Destroy(bool defer); void Destroy(bool defer);
ALWAYS_INLINE VkImage GetImage() const { return m_image; } ALWAYS_INLINE VkImage GetImage() const { return m_image; }
@ -54,6 +54,7 @@ public:
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
void Unmap() override; void Unmap() override;
void MakeReadyForSampling() override; void MakeReadyForSampling() override;
void GenerateMipmaps() override;
void SetDebugName(std::string_view name) override; void SetDebugName(std::string_view name) override;
@ -80,8 +81,8 @@ public:
VkDescriptorSet GetDescriptorSetWithSampler(VkSampler sampler); VkDescriptorSet GetDescriptorSetWithSampler(VkSampler sampler);
private: private:
VulkanTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, VkImage image, VulkanTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, Flags flags,
VmaAllocation allocation, VkImageView view, VkFormat vk_format); VkImage image, VmaAllocation allocation, VkImageView view, VkFormat vk_format);
VkCommandBuffer GetCommandBufferForUpdate(); VkCommandBuffer GetCommandBufferForUpdate();
void CopyTextureDataForUpload(void* dst, const void* src, u32 width, u32 height, u32 pitch, u32 upload_pitch) const; void CopyTextureDataForUpload(void* dst, const void* src, u32 width, u32 height, u32 pitch, u32 upload_pitch) const;
@ -159,7 +160,7 @@ public:
~VulkanDownloadTexture() override; ~VulkanDownloadTexture() override;
static std::unique_ptr<VulkanDownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format, void* memory, static std::unique_ptr<VulkanDownloadTexture> Create(u32 width, u32 height, GPUTexture::Format format, void* memory,
size_t memory_size, u32 memory_stride); size_t memory_size, u32 memory_stride, Error* error);
void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height, void CopyFromTexture(u32 dst_x, u32 dst_y, GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height,
u32 src_layer, u32 src_level, bool use_transfer_pitch) override; u32 src_layer, u32 src_level, bool use_transfer_pitch) override;