renderer_vulkan: Address more validation errors and stop memory leakage

* The transition settings are temporary until I write a proper layout tracking system
This commit is contained in:
GPUCode
2022-09-22 14:32:42 +03:00
parent f9c11eab96
commit a65f9ea5a8
17 changed files with 275 additions and 237 deletions

View File

@ -74,6 +74,7 @@ void ConfigureGraphics::SetConfiguration() {
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit);
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new); ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new);
ui->graphics_api_combo->setCurrentIndex(static_cast<int>(Settings::values.graphics_api));
} }
void ConfigureGraphics::ApplyConfiguration() { void ConfigureGraphics::ApplyConfiguration() {
@ -84,6 +85,7 @@ void ConfigureGraphics::ApplyConfiguration() {
Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked();
Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked(); Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked();
Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked(); Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked();
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(ui->graphics_api_combo->currentIndex());
} }
void ConfigureGraphics::RetranslateUI() { void ConfigureGraphics::RetranslateUI() {

View File

@ -894,7 +894,7 @@ void RasterizerCache<T>::UploadSurface(const Surface& surface, SurfaceInterval i
ASSERT(load_start >= surface->addr && load_end <= surface->end); ASSERT(load_start >= surface->addr && load_end <= surface->end);
const auto& staging = runtime.FindStaging( const auto& staging = runtime.FindStaging(
surface->width * surface->height * GetBytesPerPixel(surface->pixel_format), true); surface->width * surface->height * GetBytesPerPixel(surface->pixel_format) * 2, true);
MemoryRef source_ptr = VideoCore::g_memory->GetPhysicalRef(info.addr); MemoryRef source_ptr = VideoCore::g_memory->GetPhysicalRef(info.addr);
if (!source_ptr) [[unlikely]] { if (!source_ptr) [[unlikely]] {
return; return;

View File

@ -198,6 +198,8 @@ RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
RendererVulkan::~RendererVulkan() { RendererVulkan::~RendererVulkan() {
vk::Device device = instance.GetDevice(); vk::Device device = instance.GetDevice();
device.waitIdle();
device.destroyPipelineLayout(present_pipeline_layout); device.destroyPipelineLayout(present_pipeline_layout);
device.destroyShaderModule(present_vertex_shader); device.destroyShaderModule(present_vertex_shader);
device.destroyDescriptorSetLayout(present_descriptor_layout); device.destroyDescriptorSetLayout(present_descriptor_layout);
@ -208,9 +210,22 @@ RendererVulkan::~RendererVulkan() {
device.destroyShaderModule(present_shaders[i]); device.destroyShaderModule(present_shaders[i]);
} }
for (std::size_t i = 0; i < present_samplers.size(); i++) { for (auto& sampler : present_samplers) {
device.destroySampler(present_samplers[i]); device.destroySampler(sampler);
} }
for (auto& info : screen_infos) {
const VideoCore::HostTextureTag tag = {
.format = VideoCore::PixelFormatFromGPUPixelFormat(info.texture.format),
.width = info.texture.width,
.height = info.texture.height,
.layers = 1
};
runtime.Recycle(tag, std::move(info.texture.alloc));
}
rasterizer.reset();
} }
VideoCore::ResultStatus RendererVulkan::Init() { VideoCore::ResultStatus RendererVulkan::Init() {
@ -236,35 +251,56 @@ void RendererVulkan::Sync() {
} }
void RendererVulkan::PrepareRendertarget() { void RendererVulkan::PrepareRendertarget() {
for (int i = 0; i < 3; i++) { for (u32 i = 0; i < 3; i++) {
int fb_id = i == 2 ? 1 : 0; const u32 fb_id = i == 2 ? 1 : 0;
const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id]; const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id];
// Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04 // Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04
u32 lcd_color_addr = u32 lcd_color_addr =
(fb_id == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom); (fb_id == 0) ? LCD_REG_INDEX(color_fill_top) : LCD_REG_INDEX(color_fill_bottom);
lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr; lcd_color_addr = HW::VADDR_LCD + 4 * lcd_color_addr;
LCD::Regs::ColorFill color_fill = {0}; LCD::Regs::ColorFill color_fill{0};
LCD::Read(color_fill.raw, lcd_color_addr); LCD::Read(color_fill.raw, lcd_color_addr);
if (color_fill.is_enabled) { if (color_fill.is_enabled) {
LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b, const vk::ClearColorValue clear_color = {
screen_infos[i].texture); .float32 = std::array{
color_fill.color_r / 255.0f,
color_fill.color_g / 255.0f,
color_fill.color_b / 255.0f,
1.0f
}
};
const vk::ImageSubresourceRange range = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
TextureInfo& texture = screen_infos[i].texture;
runtime.Transition(command_buffer, texture.alloc, vk::ImageLayout::eTransferDstOptimal, 0, texture.alloc.levels);
command_buffer.clearColorImage(texture.alloc.image, vk::ImageLayout::eTransferDstOptimal,
clear_color, range);
} else { } else {
if (screen_infos[i].texture.width != framebuffer.width || TextureInfo& texture = screen_infos[i].texture;
screen_infos[i].texture.height != framebuffer.height || if (texture.width != framebuffer.width || texture.height != framebuffer.height ||
screen_infos[i].texture.format != framebuffer.color_format) { texture.format != framebuffer.color_format) {
// Reallocate texture if the framebuffer size has changed. // Reallocate texture if the framebuffer size has changed.
// This is expected to not happen very often and hence should not be a // This is expected to not happen very often and hence should not be a
// performance problem. // performance problem.
ConfigureFramebufferTexture(screen_infos[i].texture, framebuffer); ConfigureFramebufferTexture(texture, framebuffer);
} }
LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1); LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1);
// Resize the texture in case the framebuffer size has changed // Resize the texture in case the framebuffer size has changed
screen_infos[i].texture.width = framebuffer.width; texture.width = framebuffer.width;
screen_infos[i].texture.height = framebuffer.height; texture.height = framebuffer.height;
} }
} }
} }
@ -342,25 +378,6 @@ void RendererVulkan::LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& fram
} }
} }
void RendererVulkan::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture) {
const auto color = std::array<float, 4>{color_r / 255.0f, color_g / 255.0f, color_b / 255.0f, 1};
const vk::ClearColorValue clear_color = {
.float32 = color
};
const vk::ImageSubresourceRange range = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
command_buffer.clearColorImage(texture.alloc.image, vk::ImageLayout::eShaderReadOnlyOptimal,
clear_color, range);
}
void RendererVulkan::CompileShaders() { void RendererVulkan::CompileShaders() {
vk::Device device = instance.GetDevice(); vk::Device device = instance.GetDevice();
present_vertex_shader = Compile(vertex_shader, vk::ShaderStageFlagBits::eVertex, present_vertex_shader = Compile(vertex_shader, vk::ShaderStageFlagBits::eVertex,
@ -589,6 +606,30 @@ void RendererVulkan::BuildPipelines() {
} }
} }
void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer) {
TextureInfo old_texture = texture;
texture = TextureInfo {
.alloc = runtime.Allocate(framebuffer.width, framebuffer.height,
VideoCore::PixelFormatFromGPUPixelFormat(framebuffer.color_format),
VideoCore::TextureType::Texture2D),
.width = framebuffer.width,
.height = framebuffer.height,
.format = framebuffer.color_format,
};
// Recyle the old texture after allocation to avoid having duplicates of the same allocation in the recycler
if (old_texture.width != 0 && old_texture.height != 0) {
const VideoCore::HostTextureTag tag = {
.format = VideoCore::PixelFormatFromGPUPixelFormat(old_texture.format),
.width = old_texture.width,
.height = old_texture.height,
.layers = 1
};
runtime.Recycle(tag, std::move(old_texture.alloc));
}
}
void RendererVulkan::ReloadSampler() { void RendererVulkan::ReloadSampler() {
current_sampler = !Settings::values.filter_mode; current_sampler = !Settings::values.filter_mode;
} }
@ -610,16 +651,6 @@ void RendererVulkan::ReloadPipeline() {
} }
} }
void RendererVulkan::ConfigureFramebufferTexture(TextureInfo& texture,
const GPU::Regs::FramebufferConfig& framebuffer) {
texture.format = framebuffer.color_format;
texture.width = framebuffer.width;
texture.height = framebuffer.height;
texture.alloc = runtime.Allocate(framebuffer.width, framebuffer.height,
VideoCore::PixelFormatFromGPUPixelFormat(framebuffer.color_format),
VideoCore::TextureType::Texture2D);
}
void RendererVulkan::DrawSingleScreenRotated(u32 screen_id, float x, float y, float w, float h) { void RendererVulkan::DrawSingleScreenRotated(u32 screen_id, float x, float y, float w, float h) {
auto& screen_info = screen_infos[screen_id]; auto& screen_info = screen_infos[screen_id];
const auto& texcoords = screen_info.display_texcoords; const auto& texcoords = screen_info.display_texcoords;
@ -659,9 +690,14 @@ void RendererVulkan::DrawSingleScreenRotated(u32 screen_id, float x, float y, fl
.color = clear_color .color = clear_color
}; };
const auto& layout = render_window.GetFramebufferLayout();
const vk::RenderPassBeginInfo begin_info = { const vk::RenderPassBeginInfo begin_info = {
.renderPass = renderpass_cache.GetPresentRenderpass(), .renderPass = renderpass_cache.GetPresentRenderpass(),
.framebuffer = swapchain.GetFramebuffer(), .framebuffer = swapchain.GetFramebuffer(),
.renderArea = vk::Rect2D{
.offset = {0, 0},
.extent = {layout.width, layout.height}
},
.clearValueCount = 1, .clearValueCount = 1,
.pClearValues = &clear_value, .pClearValues = &clear_value,
}; };
@ -894,6 +930,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
} }
} }
return;
draw_info.layer = 0; draw_info.layer = 0;
if (layout.bottom_screen_enabled) { if (layout.bottom_screen_enabled) {
if (layout.is_rotated) { if (layout.is_rotated) {
@ -989,7 +1026,7 @@ void RendererVulkan::SwapBuffers() {
for (auto& info : screen_infos) { for (auto& info : screen_infos) {
auto alloc = info.display_texture ? info.display_texture : &info.texture.alloc; auto alloc = info.display_texture ? info.display_texture : &info.texture.alloc;
runtime.Transition(command_buffer, *alloc, vk::ImageLayout::eShaderReadOnlyOptimal, 0, 1); runtime.Transition(command_buffer, *alloc, vk::ImageLayout::eShaderReadOnlyOptimal, 0, alloc->levels);
} }
DrawScreens(layout, false); DrawScreens(layout, false);

View File

@ -79,10 +79,10 @@ private:
void CompileShaders(); void CompileShaders();
void BuildLayouts(); void BuildLayouts();
void BuildPipelines(); void BuildPipelines();
void ConfigureFramebufferTexture(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer);
void ConfigureRenderPipeline(); void ConfigureRenderPipeline();
void PrepareRendertarget(); void PrepareRendertarget();
void BeginRendering(); void BeginRendering();
void ConfigureFramebufferTexture(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer);
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped); void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
void DrawSingleScreenRotated(u32 screen_id, float x, float y, float w, float h); void DrawSingleScreenRotated(u32 screen_id, float x, float y, float w, float h);
@ -96,9 +96,6 @@ private:
void LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer, void LoadFBToScreenInfo(const GPU::Regs::FramebufferConfig& framebuffer,
ScreenInfo& screen_info, bool right_eye); ScreenInfo& screen_info, bool right_eye);
/// Fills active OpenGL texture with the given RGB color.
void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture);
private: private:
Instance instance; Instance instance;
TaskScheduler scheduler; TaskScheduler scheduler;
@ -121,7 +118,7 @@ private:
u32 current_sampler = 0; u32 current_sampler = 0;
/// Display information for top and bottom screens respectively /// Display information for top and bottom screens respectively
std::array<ScreenInfo, 3> screen_infos; std::array<ScreenInfo, 3> screen_infos{};
PresentUniformData draw_info{}; PresentUniformData draw_info{};
vk::ClearColorValue clear_color{}; vk::ClearColorValue clear_color{};
}; };

View File

@ -47,7 +47,7 @@ constexpr vk::ImageUsageFlags GetImageUsage(vk::ImageAspectFlags aspect) {
return usage | vk::ImageUsageFlagBits::eStorage | return usage | vk::ImageUsageFlagBits::eStorage |
vk::ImageUsageFlagBits::eColorAttachment; vk::ImageUsageFlagBits::eColorAttachment;
} }
}; }
/// Returns a bit mask with the required features of a format with a particular aspect /// Returns a bit mask with the required features of a format with a particular aspect
constexpr vk::FormatFeatureFlags GetFormatFeatures(vk::ImageAspectFlags aspect) { constexpr vk::FormatFeatureFlags GetFormatFeatures(vk::ImageAspectFlags aspect) {
@ -63,6 +63,6 @@ constexpr vk::FormatFeatureFlags GetFormatFeatures(vk::ImageAspectFlags aspect)
return usage | vk::FormatFeatureFlagBits::eStorageImage | return usage | vk::FormatFeatureFlagBits::eStorageImage |
vk::FormatFeatureFlagBits::eColorAttachment; vk::FormatFeatureFlagBits::eColorAttachment;
} }
}; }
} // namespace Vulkan } // namespace Vulkan

View File

@ -81,6 +81,10 @@ bool Instance::IsFormatSupported(vk::Format format, vk::FormatFeatureFlags usage
} }
vk::Format Instance::GetFormatAlternative(vk::Format format) const { vk::Format Instance::GetFormatAlternative(vk::Format format) const {
if (format == vk::Format::eUndefined) {
return format;
}
vk::FormatFeatureFlags features = GetFormatFeatures(GetImageAspect(format)); vk::FormatFeatureFlags features = GetFormatFeatures(GetImageAspect(format));
if (IsFormatSupported(format, features)) { if (IsFormatSupported(format, features)) {
return format; return format;
@ -104,8 +108,8 @@ vk::Format Instance::GetFormatAlternative(vk::Format format) const {
// B4G4R4A4 is not guaranteed by the spec to support attachments // B4G4R4A4 is not guaranteed by the spec to support attachments
return GetFormatAlternative(vk::Format::eB4G4R4A4UnormPack16); return GetFormatAlternative(vk::Format::eB4G4R4A4UnormPack16);
default: default:
LOG_WARNING(Render_Vulkan, "Unable to find compatible alternative to format = {} with usage {}", LOG_WARNING(Render_Vulkan, "Format {} doesn't support attachments, falling back to RGBA8",
vk::to_string(format), vk::to_string(features)); vk::to_string(format));
return vk::Format::eR8G8B8A8Unorm; return vk::Format::eR8G8B8A8Unorm;
} }
} }

View File

@ -150,6 +150,7 @@ PipelineCache::~PipelineCache() {
SaveDiskCache(); SaveDiskCache();
device.destroyPipelineLayout(layout); device.destroyPipelineLayout(layout);
device.destroyShaderModule(trivial_vertex_shader);
for (std::size_t i = 0; i < MAX_DESCRIPTOR_SETS; i++) { for (std::size_t i = 0; i < MAX_DESCRIPTOR_SETS; i++) {
device.destroyDescriptorSetLayout(descriptor_set_layouts[i]); device.destroyDescriptorSetLayout(descriptor_set_layouts[i]);
device.destroyDescriptorUpdateTemplate(update_templates[i]); device.destroyDescriptorUpdateTemplate(update_templates[i]);
@ -624,9 +625,9 @@ void PipelineCache::LoadDiskCache() {
void PipelineCache::SaveDiskCache() { void PipelineCache::SaveDiskCache() {
const std::string cache_path = const std::string cache_path =
FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "vulkan" + DIR_SEP "pipelines.bin"; FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + "vulkan" + DIR_SEP "pipelines.bin";
FileUtil::IOFile cache_file{cache_path, "w"}; FileUtil::IOFile cache_file{cache_path, "wb"};
if (!cache_file.IsOpen()) { if (!cache_file.IsOpen()) {
LOG_INFO(Render_Vulkan, "Unable to open pipeline cache for writing"); LOG_INFO(Render_Vulkan, "Unable to open pipeline cache for writing");
return; return;

View File

@ -172,9 +172,11 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instan
} }
RasterizerVulkan::~RasterizerVulkan() { RasterizerVulkan::~RasterizerVulkan() {
// Submit any remaining work
scheduler.Submit(true, false);
VmaAllocator allocator = instance.GetAllocator(); VmaAllocator allocator = instance.GetAllocator();
vk::Device device = instance.GetDevice(); vk::Device device = instance.GetDevice();
device.waitIdle();
for (auto& [key, sampler] : samplers) { for (auto& [key, sampler] : samplers) {
device.destroySampler(sampler); device.destroySampler(sampler);
@ -540,11 +542,6 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
MICROPROFILE_SCOPE(OpenGL_Drawing); MICROPROFILE_SCOPE(OpenGL_Drawing);
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
pipeline_info.color_attachment =
VideoCore::PixelFormatFromColorFormat(regs.framebuffer.framebuffer.color_format);
pipeline_info.depth_attachment =
VideoCore::PixelFormatFromDepthFormat(regs.framebuffer.framebuffer.depth_format);
const bool shadow_rendering = regs.framebuffer.output_merger.fragment_operation_mode == const bool shadow_rendering = regs.framebuffer.output_merger.fragment_operation_mode ==
Pica::FramebufferRegs::FragmentOperationMode::Shadow; Pica::FramebufferRegs::FragmentOperationMode::Shadow;
const bool has_stencil = const bool has_stencil =
@ -573,6 +570,11 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
auto [color_surface, depth_surface, surfaces_rect] = auto [color_surface, depth_surface, surfaces_rect] =
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect_unscaled); res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect_unscaled);
pipeline_info.color_attachment =
color_surface ? color_surface->pixel_format : VideoCore::PixelFormat::Invalid;
pipeline_info.depth_attachment =
depth_surface ? depth_surface->pixel_format : VideoCore::PixelFormat::Invalid;
const u16 res_scale = color_surface != nullptr const u16 res_scale = color_surface != nullptr
? color_surface->res_scale ? color_surface->res_scale
: (depth_surface == nullptr ? 1u : depth_surface->res_scale); : (depth_surface == nullptr ? 1u : depth_surface->res_scale);
@ -635,6 +637,43 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
} }
}; };
const auto BindCubeFace = [&](Pica::TexturingRegs::CubeFace face,
Pica::Texture::TextureInfo& info) {
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
auto surface = res_cache.GetTextureSurface(info);
const u32 binding = static_cast<u32>(face);
if (surface != nullptr) {
pipeline_cache.BindStorageImage(binding, surface->alloc.image_view);
} else {
pipeline_cache.BindStorageImage(binding, default_texture.image_view);
}
};
const auto BindSampler = [&](u32 binding, SamplerInfo& info,
const Pica::TexturingRegs::FullTextureConfig& texture) {
info = SamplerInfo{
.mag_filter = texture.config.mag_filter,
.min_filter = texture.config.min_filter,
.mip_filter = texture.config.mip_filter,
.wrap_s = texture.config.wrap_s,
.wrap_t = texture.config.wrap_t,
.border_color = texture.config.border_color.raw,
.lod_min = texture.config.lod.min_level,
.lod_max = texture.config.lod.max_level,
.lod_bias = texture.config.lod.bias
};
// Search the cache and bind the appropriate sampler
if (auto it = samplers.find(info); it != samplers.end()) {
pipeline_cache.BindSampler(binding, it->second);
} else {
vk::Sampler texture_sampler = CreateSampler(info);
samplers.emplace(info, texture_sampler);
pipeline_cache.BindSampler(binding, texture_sampler);
}
};
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
// Sync and bind the texture surfaces // Sync and bind the texture surfaces
@ -643,136 +682,69 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
const auto& texture = pica_textures[texture_index]; const auto& texture = pica_textures[texture_index];
if (texture.enabled) { if (texture.enabled) {
/*if (texture_index == 0) { if (texture_index == 0) {
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
switch (texture.config.type.Value()) { switch (texture.config.type.Value()) {
case TextureType::Shadow2D: { case TextureType::Shadow2D: {
if (!allow_shadow) auto surface = res_cache.GetTextureSurface(texture);
continue;
Surface surface = res_cache.GetTextureSurface(texture);
if (surface != nullptr) { if (surface != nullptr) {
CheckBarrier(state.image_shadow_texture_px = surface->texture.handle); pipeline_cache.BindStorageImage(0, surface->alloc.image_view);
} else { } else {
state.image_shadow_texture_px = 0; pipeline_cache.BindStorageImage(0, default_texture.image_view);
} }
continue; continue;
} }
case TextureType::ShadowCube: { case TextureType::ShadowCube: {
if (!allow_shadow)
continue;
Pica::Texture::TextureInfo info = Pica::Texture::TextureInfo::FromPicaRegister(
texture.config, texture.format);
Surface surface;
using CubeFace = Pica::TexturingRegs::CubeFace; using CubeFace = Pica::TexturingRegs::CubeFace;
info.physical_address = auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config,
regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX); texture.format);
surface = res_cache.GetTextureSurface(info); BindCubeFace(CubeFace::PositiveX, info);
if (surface != nullptr) { BindCubeFace(CubeFace::NegativeX, info);
CheckBarrier(state.image_shadow_texture_px = surface->texture.handle); BindCubeFace(CubeFace::PositiveY, info);
} else { BindCubeFace(CubeFace::NegativeY, info);
state.image_shadow_texture_px = 0; BindCubeFace(CubeFace::PositiveZ, info);
} BindCubeFace(CubeFace::NegativeZ, info);
info.physical_address =
regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX);
surface = res_cache.GetTextureSurface(info);
if (surface != nullptr) {
CheckBarrier(state.image_shadow_texture_nx = surface->texture.handle);
} else {
state.image_shadow_texture_nx = 0;
}
info.physical_address =
regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY);
surface = res_cache.GetTextureSurface(info);
if (surface != nullptr) {
CheckBarrier(state.image_shadow_texture_py = surface->texture.handle);
} else {
state.image_shadow_texture_py = 0;
}
info.physical_address =
regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY);
surface = res_cache.GetTextureSurface(info);
if (surface != nullptr) {
CheckBarrier(state.image_shadow_texture_ny = surface->texture.handle);
} else {
state.image_shadow_texture_ny = 0;
}
info.physical_address =
regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ);
surface = res_cache.GetTextureSurface(info);
if (surface != nullptr) {
CheckBarrier(state.image_shadow_texture_pz = surface->texture.handle);
} else {
state.image_shadow_texture_pz = 0;
}
info.physical_address =
regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ);
surface = res_cache.GetTextureSurface(info);
if (surface != nullptr) {
CheckBarrier(state.image_shadow_texture_nz = surface->texture.handle);
} else {
state.image_shadow_texture_nz = 0;
}
continue; continue;
} }
case TextureType::TextureCube: case TextureType::TextureCube: {
using CubeFace = Pica::TexturingRegs::CubeFace; using CubeFace = Pica::TexturingRegs::CubeFace;
TextureCubeConfig config; const VideoCore::TextureCubeConfig config = {
config.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX); .px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX),
config.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX); .nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX),
config.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY); .py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY),
config.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY); .ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY),
config.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ); .pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
config.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ); .nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
config.width = texture.config.width; .width = texture.config.width,
config.format = texture.format; .format = texture.format
state.texture_cube_unit.texture_cube = };
res_cache.GetTextureCube(config).texture.handle;
texture_cube_sampler.SyncWithConfig(texture.config); auto surface = res_cache.GetTextureCube(config);
state.texture_units[texture_index].texture_2d = 0; if (surface != nullptr) {
runtime.Transition(command_buffer, surface->alloc,
vk::ImageLayout::eShaderReadOnlyOptimal,
0, surface->alloc.levels, 0, 6);
pipeline_cache.BindTexture(3, surface->alloc.image_view);
} else {
pipeline_cache.BindTexture(3, default_texture.image_view);
}
BindSampler(3, texture_cube_sampler, texture);
continue; // Texture unit 0 setup finished. Continue to next unit continue; // Texture unit 0 setup finished. Continue to next unit
default:
state.texture_cube_unit.texture_cube = 0;
} }
}*/ default:
break;
//texture_samplers[texture_index].SyncWithConfig(texture.config); }
}
// Update sampler key // Update sampler key
texture_samplers[texture_index] = SamplerInfo{ BindSampler(texture_index, texture_samplers[texture_index], texture);
.mag_filter = texture.config.mag_filter,
.min_filter = texture.config.min_filter,
.mip_filter = texture.config.mip_filter,
.wrap_s = texture.config.wrap_s,
.wrap_t = texture.config.wrap_t,
.border_color = texture.config.border_color.raw,
.lod_min = texture.config.lod.min_level,
.lod_max = texture.config.lod.max_level,
.lod_bias = texture.config.lod.bias
};
// Search the cache and bind the appropriate sampler
const SamplerInfo& key = texture_samplers[texture_index];
if (auto it = samplers.find(key); it != samplers.end()) {
pipeline_cache.BindSampler(texture_index, it->second);
} else {
vk::Sampler texture_sampler = CreateSampler(key);
samplers.emplace(key, texture_sampler);
pipeline_cache.BindSampler(texture_index, texture_sampler);
}
auto surface = res_cache.GetTextureSurface(texture); auto surface = res_cache.GetTextureSurface(texture);
if (surface != nullptr) { if (surface != nullptr) {
runtime.Transition(command_buffer, surface->alloc, runtime.Transition(command_buffer, surface->alloc,
vk::ImageLayout::eShaderReadOnlyOptimal, 0, surface->alloc.levels); vk::ImageLayout::eShaderReadOnlyOptimal,
0, surface->alloc.levels);
CheckBarrier(surface->alloc.image_view, texture_index); CheckBarrier(surface->alloc.image_view, texture_index);
} else { } else {
// Can occur when texture addr is null or its memory is unmapped/invalid // Can occur when texture addr is null or its memory is unmapped/invalid
@ -808,6 +780,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
// Enable scissor test to prevent drawing outside of the framebuffer region // Enable scissor test to prevent drawing outside of the framebuffer region
pipeline_cache.SetScissor(draw_rect.left, draw_rect.bottom, draw_rect.GetWidth(), draw_rect.GetHeight()); pipeline_cache.SetScissor(draw_rect.left, draw_rect.bottom, draw_rect.GetWidth(), draw_rect.GetHeight());
//return true;
auto valid_surface = color_surface ? color_surface : depth_surface; auto valid_surface = color_surface ? color_surface : depth_surface;
const FramebufferInfo framebuffer_info = { const FramebufferInfo framebuffer_info = {
.color = color_surface ? color_surface->alloc.image_view : VK_NULL_HANDLE, .color = color_surface ? color_surface->alloc.image_view : VK_NULL_HANDLE,
@ -1599,10 +1573,13 @@ vk::Sampler RasterizerVulkan::CreateSampler(const SamplerInfo& info) {
.mipmapMode = PicaToVK::TextureMipFilterMode(info.mip_filter), .mipmapMode = PicaToVK::TextureMipFilterMode(info.mip_filter),
.addressModeU = PicaToVK::WrapMode(info.wrap_s), .addressModeU = PicaToVK::WrapMode(info.wrap_s),
.addressModeV = PicaToVK::WrapMode(info.wrap_t), .addressModeV = PicaToVK::WrapMode(info.wrap_t),
.mipLodBias = info.lod_bias / 256.0f,
.anisotropyEnable = true, .anisotropyEnable = true,
.maxAnisotropy = properties.limits.maxSamplerAnisotropy, .maxAnisotropy = properties.limits.maxSamplerAnisotropy,
.compareEnable = false, .compareEnable = false,
.compareOp = vk::CompareOp::eAlways, .compareOp = vk::CompareOp::eAlways,
.minLod = static_cast<float>(info.lod_min),
.maxLod = static_cast<float>(info.lod_max),
.borderColor = vk::BorderColor::eIntOpaqueBlack, .borderColor = vk::BorderColor::eIntOpaqueBlack,
.unnormalizedCoordinates = false .unnormalizedCoordinates = false
}; };

View File

@ -13,19 +13,20 @@ namespace Vulkan {
vk::Format ToVkFormatColor(u32 index) { vk::Format ToVkFormatColor(u32 index) {
switch (index) { switch (index) {
case 1: return vk::Format::eR8G8B8A8Unorm; case 0: return vk::Format::eR8G8B8A8Unorm;
case 2: return vk::Format::eR8G8B8Unorm; case 1: return vk::Format::eR8G8B8Unorm;
case 3: return vk::Format::eR5G5B5A1UnormPack16; case 2: return vk::Format::eR5G5B5A1UnormPack16;
case 4: return vk::Format::eR5G6B5UnormPack16; case 3: return vk::Format::eR5G6B5UnormPack16;
case 5: return vk::Format::eR4G4B4A4UnormPack16; case 4: return vk::Format::eR4G4B4A4UnormPack16;
default: return vk::Format::eUndefined; default: return vk::Format::eUndefined;
} }
} }
vk::Format ToVkFormatDepth(u32 index) { vk::Format ToVkFormatDepth(u32 index) {
switch (index) { switch (index) {
case 1: return vk::Format::eD16Unorm; case 0: return vk::Format::eD16Unorm;
case 2: return vk::Format::eX8D24UnormPack32; case 1: return vk::Format::eX8D24UnormPack32;
// Notice the similar gap in PixelFormat
case 3: return vk::Format::eD24UnormS8Uint; case 3: return vk::Format::eD24UnormS8Uint;
default: return vk::Format::eUndefined; default: return vk::Format::eUndefined;
} }
@ -36,14 +37,15 @@ RenderpassCache::RenderpassCache(const Instance& instance, TaskScheduler& schedu
// Pre-create all needed renderpasses by the renderer // Pre-create all needed renderpasses by the renderer
for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) { for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) {
for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) { for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) {
if (color == 0 && depth == 0) {
continue;
}
const vk::Format color_format = const vk::Format color_format =
color == 0 ? vk::Format::eUndefined : instance.GetFormatAlternative(ToVkFormatColor(color)); instance.GetFormatAlternative(ToVkFormatColor(color));
const vk::Format depth_stencil_format = const vk::Format depth_stencil_format =
depth == 0 ? vk::Format::eUndefined : instance.GetFormatAlternative(ToVkFormatDepth(depth)); instance.GetFormatAlternative(ToVkFormatDepth(depth));
if (color_format == vk::Format::eUndefined &&
depth_stencil_format == vk::Format::eUndefined) {
continue;
}
cached_renderpasses[color][depth][0] = CreateRenderPass(color_format, depth_stencil_format, cached_renderpasses[color][depth][0] = CreateRenderPass(color_format, depth_stencil_format,
vk::AttachmentLoadOp::eLoad, vk::AttachmentLoadOp::eLoad,
@ -61,16 +63,13 @@ RenderpassCache::~RenderpassCache() {
vk::Device device = instance.GetDevice(); vk::Device device = instance.GetDevice();
for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) { for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) {
for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) { for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) {
if (color == 0 && depth == 0) { if (vk::RenderPass load_pass = cached_renderpasses[color][depth][0]; load_pass) {
continue; device.destroyRenderPass(load_pass);
} }
auto& load_pass = cached_renderpasses[color][depth][0]; if (vk::RenderPass clear_pass = cached_renderpasses[color][depth][1]; clear_pass) {
auto& clear_pass = cached_renderpasses[color][depth][1]; device.destroyRenderPass(clear_pass);
}
// Destroy renderpasses
device.destroyRenderPass(load_pass);
device.destroyRenderPass(clear_pass);
} }
} }
@ -109,9 +108,9 @@ void RenderpassCache::CreatePresentRenderpass(vk::Format format) {
vk::RenderPass RenderpassCache::GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth, vk::RenderPass RenderpassCache::GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth,
bool is_clear) const { bool is_clear) const {
const u32 color_index = const u32 color_index =
color == VideoCore::PixelFormat::Invalid ? 0 : (static_cast<u32>(color) + 1); color == VideoCore::PixelFormat::Invalid ? MAX_COLOR_FORMATS : static_cast<u32>(color);
const u32 depth_index = const u32 depth_index =
depth == VideoCore::PixelFormat::Invalid ? 0 : (static_cast<u32>(depth) - 14); depth == VideoCore::PixelFormat::Invalid ? MAX_DEPTH_FORMATS : (static_cast<u32>(depth) - 14);
ASSERT(color_index <= MAX_COLOR_FORMATS && depth_index <= MAX_DEPTH_FORMATS); ASSERT(color_index <= MAX_COLOR_FORMATS && depth_index <= MAX_DEPTH_FORMATS);
return cached_renderpasses[color_index][depth_index][is_clear]; return cached_renderpasses[color_index][depth_index][is_clear];
@ -120,7 +119,6 @@ vk::RenderPass RenderpassCache::GetRenderpass(VideoCore::PixelFormat color, Vide
vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format depth, vk::AttachmentLoadOp load_op, vk::RenderPass RenderpassCache::CreateRenderPass(vk::Format color, vk::Format depth, vk::AttachmentLoadOp load_op,
vk::ImageLayout initial_layout, vk::ImageLayout final_layout) const { vk::ImageLayout initial_layout, vk::ImageLayout final_layout) const {
// Define attachments // Define attachments
u32 attachment_count = 0; u32 attachment_count = 0;
std::array<vk::AttachmentDescription, 2> attachments; std::array<vk::AttachmentDescription, 2> attachments;

View File

@ -13,7 +13,7 @@ class Instance;
class TaskScheduler; class TaskScheduler;
constexpr u32 MAX_COLOR_FORMATS = 5; constexpr u32 MAX_COLOR_FORMATS = 5;
constexpr u32 MAX_DEPTH_FORMATS = 3; constexpr u32 MAX_DEPTH_FORMATS = 4;
class RenderpassCache { class RenderpassCache {
public: public:
@ -27,11 +27,11 @@ public:
void ExitRenderpass(); void ExitRenderpass();
/// Returns the renderpass associated with the color-depth format pair /// Returns the renderpass associated with the color-depth format pair
vk::RenderPass GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth, [[nodiscard]] vk::RenderPass GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth,
bool is_clear) const; bool is_clear) const;
/// Returns the swapchain clear renderpass /// Returns the swapchain clear renderpass
vk::RenderPass GetPresentRenderpass() const { [[nodiscard]] vk::RenderPass GetPresentRenderpass() const {
return present_renderpass; return present_renderpass;
} }

View File

@ -1568,6 +1568,7 @@ void main() {
normquat = vert_normquat; normquat = vert_normquat;
view = vert_view; view = vert_view;
gl_Position = vert_position; gl_Position = vert_position;
gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0;
#if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance) #if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance)
gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
gl_ClipDistance[1] = dot(clip_coef, vert_position); gl_ClipDistance[1] = dot(clip_coef, vert_position);

View File

@ -171,7 +171,6 @@ void StreamBuffer::Commit(u32 size) {
command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, stage_mask, command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, stage_mask,
vk::DependencyFlagBits::eByRegion, {}, buffer_barrier, {}); vk::DependencyFlagBits::eByRegion, {}, buffer_barrier, {});
buffer_offset += size; buffer_offset += size;
available_size -= size; available_size -= size;
} }

View File

@ -24,6 +24,11 @@ Swapchain::~Swapchain() {
device.destroySemaphore(render_finished); device.destroySemaphore(render_finished);
device.destroySemaphore(image_available); device.destroySemaphore(image_available);
device.destroySwapchainKHR(swapchain); device.destroySwapchainKHR(swapchain);
for (auto& image : swapchain_images) {
device.destroyImageView(image.image_view);
device.destroyFramebuffer(image.framebuffer);
}
} }
void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) { void Swapchain::Create(u32 width, u32 height, bool vsync_enabled) {

View File

@ -61,10 +61,9 @@ TaskScheduler::TaskScheduler(const Instance& instance) : instance{instance} {
} }
TaskScheduler::~TaskScheduler() { TaskScheduler::~TaskScheduler() {
// Submit any remaining work
Submit(true, false);
vk::Device device = instance.GetDevice(); vk::Device device = instance.GetDevice();
device.waitIdle();
for (const auto& command : commands) { for (const auto& command : commands) {
device.destroyFence(command.fence); device.destroyFence(command.fence);
device.destroyDescriptorPool(command.descriptor_pool); device.destroyDescriptorPool(command.descriptor_pool);
@ -96,7 +95,8 @@ void TaskScheduler::WaitFence(u32 counter) {
} }
} }
UNREACHABLE_MSG("Invalid fence counter!"); LOG_CRITICAL(Render_Vulkan,"Invalid fence counter {}!", counter);
UNREACHABLE();
} }
void TaskScheduler::Submit(bool wait_completion, bool begin_next, void TaskScheduler::Submit(bool wait_completion, bool begin_next,

View File

@ -87,7 +87,9 @@ TextureRuntime::TextureRuntime(const Instance& instance, TaskScheduler& schedule
TextureRuntime::~TextureRuntime() { TextureRuntime::~TextureRuntime() {
VmaAllocator allocator = instance.GetAllocator(); VmaAllocator allocator = instance.GetAllocator();
vk::Device device = instance.GetDevice(); vk::Device device = instance.GetDevice();
for (auto& [key, alloc] : texture_recycler) { device.waitIdle();
for (const auto& [key, alloc] : texture_recycler) {
vmaDestroyImage(allocator, alloc.image, alloc.allocation); vmaDestroyImage(allocator, alloc.image, alloc.allocation);
device.destroyImageView(alloc.image_view); device.destroyImageView(alloc.image_view);
} }
@ -180,7 +182,7 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
.baseMipLevel = 0, .baseMipLevel = 0,
.levelCount = 1, .levelCount = 1,
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = 1 .layerCount = layers
} }
}; };
@ -196,11 +198,17 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
}; };
} }
void TextureRuntime::Recycle(const VideoCore::HostTextureTag tag, ImageAlloc&& alloc) {
texture_recycler.emplace(tag, std::move(alloc));
}
void TextureRuntime::FormatConvert(VideoCore::PixelFormat format, bool upload, void TextureRuntime::FormatConvert(VideoCore::PixelFormat format, bool upload,
std::span<std::byte> source, std::span<std::byte> dest) { std::span<std::byte> source, std::span<std::byte> dest) {
const VideoCore::SurfaceType type = VideoCore::GetFormatType(format); const VideoCore::SurfaceType type = VideoCore::GetFormatType(format);
const vk::FormatFeatureFlagBits feature = ToVkFormatFeatures(type); const vk::FormatFeatureFlagBits feature = ToVkFormatFeatures(type);
if (!instance.IsFormatSupported(ToVkFormat(format), feature)) { if (instance.IsFormatSupported(ToVkFormat(format), feature)) {
std::memcpy(dest.data(), source.data(), source.size());
} else {
if (format == VideoCore::PixelFormat::RGB8 && upload) { if (format == VideoCore::PixelFormat::RGB8 && upload) {
return Pica::Texture::ConvertBGRToRGBA(source, dest); return Pica::Texture::ConvertBGRToRGBA(source, dest);
} }
@ -215,7 +223,8 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
renderpass_cache.ExitRenderpass(); renderpass_cache.ExitRenderpass();
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
Transition(command_buffer, surface.alloc, vk::ImageLayout::eTransferDstOptimal, clear.texture_level, 1); Transition(command_buffer, surface.alloc, vk::ImageLayout::eTransferDstOptimal,
0, surface.alloc.levels, 0, surface.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
// For full clears we can use vkCmdClearColorImage/vkCmdClearDepthStencilImage // For full clears we can use vkCmdClearColorImage/vkCmdClearDepthStencilImage
if (clear.texture_rect == surface.GetScaledRect()) { if (clear.texture_rect == surface.GetScaledRect()) {
@ -281,8 +290,8 @@ bool TextureRuntime::CopyTextures(Surface& source, Surface& dest, const VideoCor
}; };
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, copy.src_level, 1); Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, 0, source.alloc.levels);
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, copy.dst_level, 1); Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, 0, dest.alloc.levels);
command_buffer.copyImage(source.alloc.image, vk::ImageLayout::eTransferSrcOptimal, command_buffer.copyImage(source.alloc.image, vk::ImageLayout::eTransferSrcOptimal,
dest.alloc.image, vk::ImageLayout::eTransferDstOptimal, image_copy); dest.alloc.image, vk::ImageLayout::eTransferDstOptimal, image_copy);
@ -294,8 +303,10 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCor
renderpass_cache.ExitRenderpass(); renderpass_cache.ExitRenderpass();
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, blit.src_level, 1); Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal,
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, blit.dst_level, 1); 0, source.alloc.levels, 0, source.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal,
0, dest.alloc.levels, 0, dest.texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
const std::array source_offsets = { const std::array source_offsets = {
vk::Offset3D{static_cast<s32>(blit.src_rect.left), static_cast<s32>(blit.src_rect.bottom), 0}, vk::Offset3D{static_cast<s32>(blit.src_rect.left), static_cast<s32>(blit.src_rect.bottom), 0},
@ -380,7 +391,8 @@ void TextureRuntime::GenerateMipmaps(Surface& surface, u32 max_level) {
} }
void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& alloc, void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& alloc,
vk::ImageLayout new_layout, u32 level, u32 level_count) { vk::ImageLayout new_layout, u32 level, u32 level_count,
u32 layer, u32 layer_count) {
if (new_layout == alloc.layout || !alloc.image) { if (new_layout == alloc.layout || !alloc.image) {
return; return;
} }
@ -460,10 +472,10 @@ void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& al
.image = alloc.image, .image = alloc.image,
.subresourceRange = { .subresourceRange = {
.aspectMask = alloc.aspect, .aspectMask = alloc.aspect,
.baseMipLevel = level, .baseMipLevel = /*level*/0,
.levelCount = level_count, .levelCount = /*level_count*/alloc.levels,
.baseArrayLayer = 0, .baseArrayLayer = layer,
.layerCount = 1 .layerCount = layer_count
} }
}; };
@ -477,20 +489,23 @@ void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& al
Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime) Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime)
: VideoCore::SurfaceBase<Surface>{params}, runtime{runtime}, instance{runtime.GetInstance()}, : VideoCore::SurfaceBase<Surface>{params}, runtime{runtime}, instance{runtime.GetInstance()},
scheduler{runtime.GetScheduler()} { scheduler{runtime.GetScheduler()} {
if (params.pixel_format != VideoCore::PixelFormat::Invalid) {
if (pixel_format != VideoCore::PixelFormat::Invalid) {
alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), params.pixel_format, texture_type); alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), params.pixel_format, texture_type);
} }
} }
Surface::~Surface() { Surface::~Surface() {
const VideoCore::HostTextureTag tag = { if (pixel_format != VideoCore::PixelFormat::Invalid) {
.format = pixel_format, const VideoCore::HostTextureTag tag = {
.width = GetScaledWidth(), .format = pixel_format,
.height = GetScaledHeight(), .width = GetScaledWidth(),
.layers = texture_type == VideoCore::TextureType::CubeMap ? 6u : 1u .height = GetScaledHeight(),
}; .layers = texture_type == VideoCore::TextureType::CubeMap ? 6u : 1u
};
runtime.texture_recycler.emplace(tag, std::move(alloc)); runtime.Recycle(tag, std::move(alloc));
}
} }
MICROPROFILE_DEFINE(Vulkan_Upload, "VulkanSurface", "Texture Upload", MP_RGB(128, 192, 64)); MICROPROFILE_DEFINE(Vulkan_Upload, "VulkanSurface", "Texture Upload", MP_RGB(128, 192, 64));
@ -517,7 +532,8 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingDa
.imageExtent = {rect.GetWidth(), rect.GetHeight(), 1} .imageExtent = {rect.GetWidth(), rect.GetHeight(), 1}
}; };
runtime.Transition(command_buffer, alloc, vk::ImageLayout::eTransferDstOptimal, upload.texture_level, 1); runtime.Transition(command_buffer, alloc, vk::ImageLayout::eTransferDstOptimal, 0, alloc.levels,
0, texture_type == VideoCore::TextureType::CubeMap ? 6 : 1);
command_buffer.copyBufferToImage(staging.buffer, alloc.image, command_buffer.copyBufferToImage(staging.buffer, alloc.image,
vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eTransferDstOptimal,
copy_region); copy_region);

View File

@ -44,15 +44,15 @@ public:
RenderpassCache& renderpass_cache); RenderpassCache& renderpass_cache);
~TextureRuntime(); ~TextureRuntime();
TextureRuntime(const TextureRuntime&) = delete;
TextureRuntime& operator=(const TextureRuntime&) = delete;
/// Maps an internal staging buffer of the provided size of pixel uploads/downloads /// Maps an internal staging buffer of the provided size of pixel uploads/downloads
StagingData FindStaging(u32 size, bool upload); [[nodiscard]] StagingData FindStaging(u32 size, bool upload);
/// Allocates a vulkan image possibly resusing an existing one /// Allocates a vulkan image possibly resusing an existing one
ImageAlloc Allocate(u32 width, u32 height, VideoCore::PixelFormat format, [[nodiscard]] ImageAlloc Allocate(u32 width, u32 height, VideoCore::PixelFormat format,
VideoCore::TextureType type); VideoCore::TextureType type);
/// Takes back ownership of the allocation for recycling
void Recycle(const VideoCore::HostTextureTag tag, ImageAlloc&& alloc);
/// Performs required format convertions on the staging data /// Performs required format convertions on the staging data
void FormatConvert(VideoCore::PixelFormat format, bool upload, void FormatConvert(VideoCore::PixelFormat format, bool upload,
@ -60,10 +60,8 @@ public:
/// Transitions the mip level range of the surface to new_layout /// Transitions the mip level range of the surface to new_layout
void Transition(vk::CommandBuffer command_buffer, ImageAlloc& alloc, void Transition(vk::CommandBuffer command_buffer, ImageAlloc& alloc,
vk::ImageLayout new_layout, u32 level, u32 level_count); vk::ImageLayout new_layout, u32 level, u32 level_count,
u32 layer = 0, u32 layer_count = 1);
/// Performs operations that need to be done on every scheduler slot switch
void OnSlotSwitch(u32 new_slot);
/// Fills the rectangle of the texture with the clear value provided /// Fills the rectangle of the texture with the clear value provided
bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear, bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
@ -78,6 +76,9 @@ public:
/// Generates mipmaps for all the available levels of the texture /// Generates mipmaps for all the available levels of the texture
void GenerateMipmaps(Surface& surface, u32 max_level); void GenerateMipmaps(Surface& surface, u32 max_level);
/// Performs operations that need to be done on every scheduler slot switch
void OnSlotSwitch(u32 new_slot);
private: private:
/// Returns the current Vulkan instance /// Returns the current Vulkan instance
const Instance& GetInstance() const { const Instance& GetInstance() const {
@ -95,7 +96,7 @@ private:
RenderpassCache& renderpass_cache; RenderpassCache& renderpass_cache;
std::array<std::unique_ptr<StagingBuffer>, SCHEDULER_COMMAND_COUNT> staging_buffers; std::array<std::unique_ptr<StagingBuffer>, SCHEDULER_COMMAND_COUNT> staging_buffers;
std::array<u32, SCHEDULER_COMMAND_COUNT> staging_offsets{}; std::array<u32, SCHEDULER_COMMAND_COUNT> staging_offsets{};
std::unordered_map<VideoCore::HostTextureTag, ImageAlloc> texture_recycler; std::unordered_multimap<VideoCore::HostTextureTag, ImageAlloc> texture_recycler;
}; };
class Surface : public VideoCore::SurfaceBase<Surface> { class Surface : public VideoCore::SurfaceBase<Surface> {

View File

@ -233,7 +233,7 @@ void ConvertBGRToRGB(std::span<const std::byte> source, std::span<std::byte> des
void ConvertBGRToRGBA(std::span<const std::byte> source, std::span<std::byte> dest) { void ConvertBGRToRGBA(std::span<const std::byte> source, std::span<std::byte> dest) {
u32 j = 0; u32 j = 0;
for (u32 i = 0; i < source.size(); i += 3) { for (std::size_t i = 0; i < source.size(); i += 3) {
dest[j] = source[i + 2]; dest[j] = source[i + 2];
dest[j + 1] = source[i + 1]; dest[j + 1] = source[i + 1];
dest[j + 2] = source[i]; dest[j + 2] = source[i];