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:
@ -74,6 +74,7 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit);
|
||||
ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);
|
||||
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() {
|
||||
@ -84,6 +85,7 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
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_vsync_new = ui->toggle_vsync_new->isChecked();
|
||||
Settings::values.graphics_api = static_cast<Settings::GraphicsAPI>(ui->graphics_api_combo->currentIndex());
|
||||
}
|
||||
|
||||
void ConfigureGraphics::RetranslateUI() {
|
||||
|
@ -894,7 +894,7 @@ void RasterizerCache<T>::UploadSurface(const Surface& surface, SurfaceInterval i
|
||||
ASSERT(load_start >= surface->addr && load_end <= surface->end);
|
||||
|
||||
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);
|
||||
if (!source_ptr) [[unlikely]] {
|
||||
return;
|
||||
|
@ -198,6 +198,8 @@ RendererVulkan::RendererVulkan(Frontend::EmuWindow& window)
|
||||
|
||||
RendererVulkan::~RendererVulkan() {
|
||||
vk::Device device = instance.GetDevice();
|
||||
device.waitIdle();
|
||||
|
||||
device.destroyPipelineLayout(present_pipeline_layout);
|
||||
device.destroyShaderModule(present_vertex_shader);
|
||||
device.destroyDescriptorSetLayout(present_descriptor_layout);
|
||||
@ -208,9 +210,22 @@ RendererVulkan::~RendererVulkan() {
|
||||
device.destroyShaderModule(present_shaders[i]);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < present_samplers.size(); i++) {
|
||||
device.destroySampler(present_samplers[i]);
|
||||
for (auto& sampler : present_samplers) {
|
||||
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() {
|
||||
@ -236,35 +251,56 @@ void RendererVulkan::Sync() {
|
||||
}
|
||||
|
||||
void RendererVulkan::PrepareRendertarget() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int fb_id = i == 2 ? 1 : 0;
|
||||
for (u32 i = 0; i < 3; i++) {
|
||||
const u32 fb_id = i == 2 ? 1 : 0;
|
||||
const auto& framebuffer = GPU::g_regs.framebuffer_config[fb_id];
|
||||
|
||||
// Main LCD (0): 0x1ED02204, Sub LCD (1): 0x1ED02A04
|
||||
u32 lcd_color_addr =
|
||||
(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::Regs::ColorFill color_fill = {0};
|
||||
LCD::Regs::ColorFill color_fill{0};
|
||||
LCD::Read(color_fill.raw, lcd_color_addr);
|
||||
|
||||
if (color_fill.is_enabled) {
|
||||
LoadColorToActiveGLTexture(color_fill.color_r, color_fill.color_g, color_fill.color_b,
|
||||
screen_infos[i].texture);
|
||||
const vk::ClearColorValue clear_color = {
|
||||
.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 {
|
||||
if (screen_infos[i].texture.width != framebuffer.width ||
|
||||
screen_infos[i].texture.height != framebuffer.height ||
|
||||
screen_infos[i].texture.format != framebuffer.color_format) {
|
||||
TextureInfo& texture = screen_infos[i].texture;
|
||||
if (texture.width != framebuffer.width || texture.height != framebuffer.height ||
|
||||
texture.format != framebuffer.color_format) {
|
||||
|
||||
// Reallocate texture if the framebuffer size has changed.
|
||||
// This is expected to not happen very often and hence should not be a
|
||||
// performance problem.
|
||||
ConfigureFramebufferTexture(screen_infos[i].texture, framebuffer);
|
||||
ConfigureFramebufferTexture(texture, framebuffer);
|
||||
}
|
||||
|
||||
LoadFBToScreenInfo(framebuffer, screen_infos[i], i == 1);
|
||||
|
||||
// Resize the texture in case the framebuffer size has changed
|
||||
screen_infos[i].texture.width = framebuffer.width;
|
||||
screen_infos[i].texture.height = framebuffer.height;
|
||||
texture.width = framebuffer.width;
|
||||
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() {
|
||||
vk::Device device = instance.GetDevice();
|
||||
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() {
|
||||
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) {
|
||||
auto& screen_info = screen_infos[screen_id];
|
||||
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
|
||||
};
|
||||
|
||||
const auto& layout = render_window.GetFramebufferLayout();
|
||||
const vk::RenderPassBeginInfo begin_info = {
|
||||
.renderPass = renderpass_cache.GetPresentRenderpass(),
|
||||
.framebuffer = swapchain.GetFramebuffer(),
|
||||
.renderArea = vk::Rect2D{
|
||||
.offset = {0, 0},
|
||||
.extent = {layout.width, layout.height}
|
||||
},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clear_value,
|
||||
};
|
||||
@ -894,6 +930,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
draw_info.layer = 0;
|
||||
if (layout.bottom_screen_enabled) {
|
||||
if (layout.is_rotated) {
|
||||
@ -989,7 +1026,7 @@ void RendererVulkan::SwapBuffers() {
|
||||
|
||||
for (auto& info : screen_infos) {
|
||||
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);
|
||||
|
@ -79,10 +79,10 @@ private:
|
||||
void CompileShaders();
|
||||
void BuildLayouts();
|
||||
void BuildPipelines();
|
||||
void ConfigureFramebufferTexture(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer);
|
||||
void ConfigureRenderPipeline();
|
||||
void PrepareRendertarget();
|
||||
void BeginRendering();
|
||||
void ConfigureFramebufferTexture(TextureInfo& texture, const GPU::Regs::FramebufferConfig& framebuffer);
|
||||
|
||||
void DrawScreens(const Layout::FramebufferLayout& layout, bool flipped);
|
||||
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,
|
||||
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:
|
||||
Instance instance;
|
||||
TaskScheduler scheduler;
|
||||
@ -121,7 +118,7 @@ private:
|
||||
u32 current_sampler = 0;
|
||||
|
||||
/// Display information for top and bottom screens respectively
|
||||
std::array<ScreenInfo, 3> screen_infos;
|
||||
std::array<ScreenInfo, 3> screen_infos{};
|
||||
PresentUniformData draw_info{};
|
||||
vk::ClearColorValue clear_color{};
|
||||
};
|
||||
|
@ -47,7 +47,7 @@ constexpr vk::ImageUsageFlags GetImageUsage(vk::ImageAspectFlags aspect) {
|
||||
return usage | vk::ImageUsageFlagBits::eStorage |
|
||||
vk::ImageUsageFlagBits::eColorAttachment;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns a bit mask with the required features of a format with a particular aspect
|
||||
constexpr vk::FormatFeatureFlags GetFormatFeatures(vk::ImageAspectFlags aspect) {
|
||||
@ -63,6 +63,6 @@ constexpr vk::FormatFeatureFlags GetFormatFeatures(vk::ImageAspectFlags aspect)
|
||||
return usage | vk::FormatFeatureFlagBits::eStorageImage |
|
||||
vk::FormatFeatureFlagBits::eColorAttachment;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@ -81,6 +81,10 @@ bool Instance::IsFormatSupported(vk::Format format, vk::FormatFeatureFlags usage
|
||||
}
|
||||
|
||||
vk::Format Instance::GetFormatAlternative(vk::Format format) const {
|
||||
if (format == vk::Format::eUndefined) {
|
||||
return format;
|
||||
}
|
||||
|
||||
vk::FormatFeatureFlags features = GetFormatFeatures(GetImageAspect(format));
|
||||
if (IsFormatSupported(format, features)) {
|
||||
return format;
|
||||
@ -104,8 +108,8 @@ vk::Format Instance::GetFormatAlternative(vk::Format format) const {
|
||||
// B4G4R4A4 is not guaranteed by the spec to support attachments
|
||||
return GetFormatAlternative(vk::Format::eB4G4R4A4UnormPack16);
|
||||
default:
|
||||
LOG_WARNING(Render_Vulkan, "Unable to find compatible alternative to format = {} with usage {}",
|
||||
vk::to_string(format), vk::to_string(features));
|
||||
LOG_WARNING(Render_Vulkan, "Format {} doesn't support attachments, falling back to RGBA8",
|
||||
vk::to_string(format));
|
||||
return vk::Format::eR8G8B8A8Unorm;
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +150,7 @@ PipelineCache::~PipelineCache() {
|
||||
SaveDiskCache();
|
||||
|
||||
device.destroyPipelineLayout(layout);
|
||||
device.destroyShaderModule(trivial_vertex_shader);
|
||||
for (std::size_t i = 0; i < MAX_DESCRIPTOR_SETS; i++) {
|
||||
device.destroyDescriptorSetLayout(descriptor_set_layouts[i]);
|
||||
device.destroyDescriptorUpdateTemplate(update_templates[i]);
|
||||
@ -624,9 +625,9 @@ void PipelineCache::LoadDiskCache() {
|
||||
|
||||
void PipelineCache::SaveDiskCache() {
|
||||
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()) {
|
||||
LOG_INFO(Render_Vulkan, "Unable to open pipeline cache for writing");
|
||||
return;
|
||||
|
@ -172,9 +172,11 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instan
|
||||
}
|
||||
|
||||
RasterizerVulkan::~RasterizerVulkan() {
|
||||
// Submit any remaining work
|
||||
scheduler.Submit(true, false);
|
||||
|
||||
VmaAllocator allocator = instance.GetAllocator();
|
||||
vk::Device device = instance.GetDevice();
|
||||
device.waitIdle();
|
||||
|
||||
for (auto& [key, sampler] : samplers) {
|
||||
device.destroySampler(sampler);
|
||||
@ -540,11 +542,6 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
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 ==
|
||||
Pica::FramebufferRegs::FragmentOperationMode::Shadow;
|
||||
const bool has_stencil =
|
||||
@ -573,6 +570,11 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
auto [color_surface, depth_surface, surfaces_rect] =
|
||||
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
|
||||
? color_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();
|
||||
|
||||
// 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];
|
||||
|
||||
if (texture.enabled) {
|
||||
/*if (texture_index == 0) {
|
||||
if (texture_index == 0) {
|
||||
using TextureType = Pica::TexturingRegs::TextureConfig::TextureType;
|
||||
switch (texture.config.type.Value()) {
|
||||
case TextureType::Shadow2D: {
|
||||
if (!allow_shadow)
|
||||
continue;
|
||||
|
||||
Surface surface = res_cache.GetTextureSurface(texture);
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
CheckBarrier(state.image_shadow_texture_px = surface->texture.handle);
|
||||
pipeline_cache.BindStorageImage(0, surface->alloc.image_view);
|
||||
} else {
|
||||
state.image_shadow_texture_px = 0;
|
||||
pipeline_cache.BindStorageImage(0, default_texture.image_view);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
info.physical_address =
|
||||
regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX);
|
||||
surface = res_cache.GetTextureSurface(info);
|
||||
if (surface != nullptr) {
|
||||
CheckBarrier(state.image_shadow_texture_px = surface->texture.handle);
|
||||
} else {
|
||||
state.image_shadow_texture_px = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
auto info = Pica::Texture::TextureInfo::FromPicaRegister(texture.config,
|
||||
texture.format);
|
||||
BindCubeFace(CubeFace::PositiveX, info);
|
||||
BindCubeFace(CubeFace::NegativeX, info);
|
||||
BindCubeFace(CubeFace::PositiveY, info);
|
||||
BindCubeFace(CubeFace::NegativeY, info);
|
||||
BindCubeFace(CubeFace::PositiveZ, info);
|
||||
BindCubeFace(CubeFace::NegativeZ, info);
|
||||
continue;
|
||||
}
|
||||
case TextureType::TextureCube:
|
||||
case TextureType::TextureCube: {
|
||||
using CubeFace = Pica::TexturingRegs::CubeFace;
|
||||
TextureCubeConfig config;
|
||||
config.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX);
|
||||
config.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX);
|
||||
config.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY);
|
||||
config.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY);
|
||||
config.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ);
|
||||
config.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ);
|
||||
config.width = texture.config.width;
|
||||
config.format = texture.format;
|
||||
state.texture_cube_unit.texture_cube =
|
||||
res_cache.GetTextureCube(config).texture.handle;
|
||||
const VideoCore::TextureCubeConfig config = {
|
||||
.px = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX),
|
||||
.nx = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX),
|
||||
.py = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY),
|
||||
.ny = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY),
|
||||
.pz = regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ),
|
||||
.nz = regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ),
|
||||
.width = texture.config.width,
|
||||
.format = texture.format
|
||||
};
|
||||
|
||||
texture_cube_sampler.SyncWithConfig(texture.config);
|
||||
state.texture_units[texture_index].texture_2d = 0;
|
||||
auto surface = res_cache.GetTextureCube(config);
|
||||
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
|
||||
default:
|
||||
state.texture_cube_unit.texture_cube = 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
//texture_samplers[texture_index].SyncWithConfig(texture.config);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update sampler key
|
||||
texture_samplers[texture_index] = 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
|
||||
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);
|
||||
}
|
||||
BindSampler(texture_index, texture_samplers[texture_index], texture);
|
||||
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
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);
|
||||
} else {
|
||||
// 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
|
||||
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;
|
||||
const FramebufferInfo framebuffer_info = {
|
||||
.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),
|
||||
.addressModeU = PicaToVK::WrapMode(info.wrap_s),
|
||||
.addressModeV = PicaToVK::WrapMode(info.wrap_t),
|
||||
.mipLodBias = info.lod_bias / 256.0f,
|
||||
.anisotropyEnable = true,
|
||||
.maxAnisotropy = properties.limits.maxSamplerAnisotropy,
|
||||
.compareEnable = false,
|
||||
.compareOp = vk::CompareOp::eAlways,
|
||||
.minLod = static_cast<float>(info.lod_min),
|
||||
.maxLod = static_cast<float>(info.lod_max),
|
||||
.borderColor = vk::BorderColor::eIntOpaqueBlack,
|
||||
.unnormalizedCoordinates = false
|
||||
};
|
||||
|
@ -13,19 +13,20 @@ namespace Vulkan {
|
||||
|
||||
vk::Format ToVkFormatColor(u32 index) {
|
||||
switch (index) {
|
||||
case 1: return vk::Format::eR8G8B8A8Unorm;
|
||||
case 2: return vk::Format::eR8G8B8Unorm;
|
||||
case 3: return vk::Format::eR5G5B5A1UnormPack16;
|
||||
case 4: return vk::Format::eR5G6B5UnormPack16;
|
||||
case 5: return vk::Format::eR4G4B4A4UnormPack16;
|
||||
case 0: return vk::Format::eR8G8B8A8Unorm;
|
||||
case 1: return vk::Format::eR8G8B8Unorm;
|
||||
case 2: return vk::Format::eR5G5B5A1UnormPack16;
|
||||
case 3: return vk::Format::eR5G6B5UnormPack16;
|
||||
case 4: return vk::Format::eR4G4B4A4UnormPack16;
|
||||
default: return vk::Format::eUndefined;
|
||||
}
|
||||
}
|
||||
|
||||
vk::Format ToVkFormatDepth(u32 index) {
|
||||
switch (index) {
|
||||
case 1: return vk::Format::eD16Unorm;
|
||||
case 2: return vk::Format::eX8D24UnormPack32;
|
||||
case 0: return vk::Format::eD16Unorm;
|
||||
case 1: return vk::Format::eX8D24UnormPack32;
|
||||
// Notice the similar gap in PixelFormat
|
||||
case 3: return vk::Format::eD24UnormS8Uint;
|
||||
default: return vk::Format::eUndefined;
|
||||
}
|
||||
@ -36,14 +37,15 @@ RenderpassCache::RenderpassCache(const Instance& instance, TaskScheduler& schedu
|
||||
// Pre-create all needed renderpasses by the renderer
|
||||
for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) {
|
||||
for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) {
|
||||
if (color == 0 && depth == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const vk::Format color_format =
|
||||
color == 0 ? vk::Format::eUndefined : instance.GetFormatAlternative(ToVkFormatColor(color));
|
||||
instance.GetFormatAlternative(ToVkFormatColor(color));
|
||||
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,
|
||||
vk::AttachmentLoadOp::eLoad,
|
||||
@ -61,16 +63,13 @@ RenderpassCache::~RenderpassCache() {
|
||||
vk::Device device = instance.GetDevice();
|
||||
for (u32 color = 0; color <= MAX_COLOR_FORMATS; color++) {
|
||||
for (u32 depth = 0; depth <= MAX_DEPTH_FORMATS; depth++) {
|
||||
if (color == 0 && depth == 0) {
|
||||
continue;
|
||||
}
|
||||
if (vk::RenderPass load_pass = cached_renderpasses[color][depth][0]; load_pass) {
|
||||
device.destroyRenderPass(load_pass);
|
||||
}
|
||||
|
||||
auto& load_pass = cached_renderpasses[color][depth][0];
|
||||
auto& clear_pass = cached_renderpasses[color][depth][1];
|
||||
|
||||
// Destroy renderpasses
|
||||
device.destroyRenderPass(load_pass);
|
||||
device.destroyRenderPass(clear_pass);
|
||||
if (vk::RenderPass clear_pass = cached_renderpasses[color][depth][1]; clear_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,
|
||||
bool is_clear) const {
|
||||
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 =
|
||||
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);
|
||||
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::ImageLayout initial_layout, vk::ImageLayout final_layout) const {
|
||||
// Define attachments
|
||||
|
||||
u32 attachment_count = 0;
|
||||
std::array<vk::AttachmentDescription, 2> attachments;
|
||||
|
||||
|
@ -13,7 +13,7 @@ class Instance;
|
||||
class TaskScheduler;
|
||||
|
||||
constexpr u32 MAX_COLOR_FORMATS = 5;
|
||||
constexpr u32 MAX_DEPTH_FORMATS = 3;
|
||||
constexpr u32 MAX_DEPTH_FORMATS = 4;
|
||||
|
||||
class RenderpassCache {
|
||||
public:
|
||||
@ -27,11 +27,11 @@ public:
|
||||
void ExitRenderpass();
|
||||
|
||||
/// Returns the renderpass associated with the color-depth format pair
|
||||
vk::RenderPass GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth,
|
||||
bool is_clear) const;
|
||||
[[nodiscard]] vk::RenderPass GetRenderpass(VideoCore::PixelFormat color, VideoCore::PixelFormat depth,
|
||||
bool is_clear) const;
|
||||
|
||||
/// Returns the swapchain clear renderpass
|
||||
vk::RenderPass GetPresentRenderpass() const {
|
||||
[[nodiscard]] vk::RenderPass GetPresentRenderpass() const {
|
||||
return present_renderpass;
|
||||
}
|
||||
|
||||
|
@ -1568,6 +1568,7 @@ void main() {
|
||||
normquat = vert_normquat;
|
||||
view = vert_view;
|
||||
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)
|
||||
gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0
|
||||
gl_ClipDistance[1] = dot(clip_coef, vert_position);
|
||||
|
@ -171,7 +171,6 @@ void StreamBuffer::Commit(u32 size) {
|
||||
command_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, stage_mask,
|
||||
vk::DependencyFlagBits::eByRegion, {}, buffer_barrier, {});
|
||||
|
||||
|
||||
buffer_offset += size;
|
||||
available_size -= size;
|
||||
}
|
||||
|
@ -24,6 +24,11 @@ Swapchain::~Swapchain() {
|
||||
device.destroySemaphore(render_finished);
|
||||
device.destroySemaphore(image_available);
|
||||
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) {
|
||||
|
@ -61,10 +61,9 @@ TaskScheduler::TaskScheduler(const Instance& instance) : instance{instance} {
|
||||
}
|
||||
|
||||
TaskScheduler::~TaskScheduler() {
|
||||
// Submit any remaining work
|
||||
Submit(true, false);
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
device.waitIdle();
|
||||
|
||||
for (const auto& command : commands) {
|
||||
device.destroyFence(command.fence);
|
||||
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,
|
||||
|
@ -87,7 +87,9 @@ TextureRuntime::TextureRuntime(const Instance& instance, TaskScheduler& schedule
|
||||
TextureRuntime::~TextureRuntime() {
|
||||
VmaAllocator allocator = instance.GetAllocator();
|
||||
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);
|
||||
device.destroyImageView(alloc.image_view);
|
||||
}
|
||||
@ -180,7 +182,7 @@ ImageAlloc TextureRuntime::Allocate(u32 width, u32 height, VideoCore::PixelForma
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.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,
|
||||
std::span<std::byte> source, std::span<std::byte> dest) {
|
||||
const VideoCore::SurfaceType type = VideoCore::GetFormatType(format);
|
||||
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) {
|
||||
return Pica::Texture::ConvertBGRToRGBA(source, dest);
|
||||
}
|
||||
@ -215,7 +223,8 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea
|
||||
renderpass_cache.ExitRenderpass();
|
||||
|
||||
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
|
||||
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();
|
||||
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, copy.src_level, 1);
|
||||
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, copy.dst_level, 1);
|
||||
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, 0, source.alloc.levels);
|
||||
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, 0, dest.alloc.levels);
|
||||
|
||||
command_buffer.copyImage(source.alloc.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
dest.alloc.image, vk::ImageLayout::eTransferDstOptimal, image_copy);
|
||||
@ -294,8 +303,10 @@ bool TextureRuntime::BlitTextures(Surface& source, Surface& dest, const VideoCor
|
||||
renderpass_cache.ExitRenderpass();
|
||||
|
||||
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
|
||||
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal, blit.src_level, 1);
|
||||
Transition(command_buffer, dest.alloc, vk::ImageLayout::eTransferDstOptimal, blit.dst_level, 1);
|
||||
Transition(command_buffer, source.alloc, vk::ImageLayout::eTransferSrcOptimal,
|
||||
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 = {
|
||||
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,
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@ -460,10 +472,10 @@ void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& al
|
||||
.image = alloc.image,
|
||||
.subresourceRange = {
|
||||
.aspectMask = alloc.aspect,
|
||||
.baseMipLevel = level,
|
||||
.levelCount = level_count,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1
|
||||
.baseMipLevel = /*level*/0,
|
||||
.levelCount = /*level_count*/alloc.levels,
|
||||
.baseArrayLayer = layer,
|
||||
.layerCount = layer_count
|
||||
}
|
||||
};
|
||||
|
||||
@ -477,20 +489,23 @@ void TextureRuntime::Transition(vk::CommandBuffer command_buffer, ImageAlloc& al
|
||||
Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime)
|
||||
: VideoCore::SurfaceBase<Surface>{params}, runtime{runtime}, instance{runtime.GetInstance()},
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Surface::~Surface() {
|
||||
const VideoCore::HostTextureTag tag = {
|
||||
.format = pixel_format,
|
||||
.width = GetScaledWidth(),
|
||||
.height = GetScaledHeight(),
|
||||
.layers = texture_type == VideoCore::TextureType::CubeMap ? 6u : 1u
|
||||
};
|
||||
if (pixel_format != VideoCore::PixelFormat::Invalid) {
|
||||
const VideoCore::HostTextureTag tag = {
|
||||
.format = pixel_format,
|
||||
.width = GetScaledWidth(),
|
||||
.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));
|
||||
@ -517,7 +532,8 @@ void Surface::Upload(const VideoCore::BufferTextureCopy& upload, const StagingDa
|
||||
.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,
|
||||
vk::ImageLayout::eTransferDstOptimal,
|
||||
copy_region);
|
||||
|
@ -44,15 +44,15 @@ public:
|
||||
RenderpassCache& renderpass_cache);
|
||||
~TextureRuntime();
|
||||
|
||||
TextureRuntime(const TextureRuntime&) = delete;
|
||||
TextureRuntime& operator=(const TextureRuntime&) = delete;
|
||||
|
||||
/// 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
|
||||
ImageAlloc Allocate(u32 width, u32 height, VideoCore::PixelFormat format,
|
||||
VideoCore::TextureType type);
|
||||
[[nodiscard]] ImageAlloc Allocate(u32 width, u32 height, VideoCore::PixelFormat format,
|
||||
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
|
||||
void FormatConvert(VideoCore::PixelFormat format, bool upload,
|
||||
@ -60,10 +60,8 @@ public:
|
||||
|
||||
/// Transitions the mip level range of the surface to new_layout
|
||||
void Transition(vk::CommandBuffer command_buffer, ImageAlloc& alloc,
|
||||
vk::ImageLayout new_layout, u32 level, u32 level_count);
|
||||
|
||||
/// Performs operations that need to be done on every scheduler slot switch
|
||||
void OnSlotSwitch(u32 new_slot);
|
||||
vk::ImageLayout new_layout, u32 level, u32 level_count,
|
||||
u32 layer = 0, u32 layer_count = 1);
|
||||
|
||||
/// Fills the rectangle of the texture with the clear value provided
|
||||
bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear,
|
||||
@ -78,6 +76,9 @@ public:
|
||||
/// Generates mipmaps for all the available levels of the texture
|
||||
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:
|
||||
/// Returns the current Vulkan instance
|
||||
const Instance& GetInstance() const {
|
||||
@ -95,7 +96,7 @@ private:
|
||||
RenderpassCache& renderpass_cache;
|
||||
std::array<std::unique_ptr<StagingBuffer>, SCHEDULER_COMMAND_COUNT> staging_buffers;
|
||||
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> {
|
||||
|
@ -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) {
|
||||
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 + 1] = source[i + 1];
|
||||
dest[j + 2] = source[i];
|
||||
|
Reference in New Issue
Block a user