renderer_vulkan: Fix mipmap generation

This commit is contained in:
emufan
2022-06-19 12:44:44 +03:00
parent 69bde2b13b
commit 18cc68c687
5 changed files with 75 additions and 34 deletions

View File

@ -328,6 +328,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
} }
// Sync and bind the texture surfaces // Sync and bind the texture surfaces
VKTexture temp_tex;
const auto pica_textures = regs.texturing.GetTextures(); const auto pica_textures = regs.texturing.GetTextures();
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
const auto& texture = pica_textures[texture_index]; const auto& texture = pica_textures[texture_index];
@ -336,7 +337,16 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
//texture_samplers[texture_index].SyncWithConfig(texture.config); //texture_samplers[texture_index].SyncWithConfig(texture.config);
Surface surface = res_cache.GetTextureSurface(texture); Surface surface = res_cache.GetTextureSurface(texture);
if (surface != nullptr) { if (surface != nullptr) {
if (color_surface && color_surface->texture.GetHandle() ==
surface->texture.GetHandle()) {
// The game is trying to use a surface as a texture and framebuffer at the same time
// which causes unpredictable behavior on the host.
// Making a copy to sample from eliminates this issue and seems to be fairly cheap.
temp_tex.Create(color_surface->texture);
state.SetTexture(texture_index, temp_tex);
} else {
state.SetTexture(texture_index, surface->texture); state.SetTexture(texture_index, surface->texture);
}
} 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
// HACK: In this case, the correct behaviour for the PICA is to use the last // HACK: In this case, the correct behaviour for the PICA is to use the last
@ -1141,6 +1151,9 @@ void RasterizerVulkan::SyncCullMode() {
const auto& regs = Pica::g_state.regs; const auto& regs = Pica::g_state.regs;
auto& state = VulkanState::Get(); auto& state = VulkanState::Get();
state.SetCullMode(vk::CullModeFlagBits::eNone);
return;
switch (regs.rasterizer.cull_mode) { switch (regs.rasterizer.cull_mode) {
case Pica::RasterizerRegs::CullMode::KeepAll: case Pica::RasterizerRegs::CullMode::KeepAll:
state.SetCullMode(vk::CullModeFlagBits::eNone); state.SetCullMode(vk::CullModeFlagBits::eNone);

View File

@ -239,23 +239,28 @@ inline vk::ImageSubresourceRange SubResourceLayersToRange(const vk::ImageSubreso
} }
static bool BlitTextures(const Surface& src_surface, const Common::Rectangle<u32>& src_rect, static bool BlitTextures(const Surface& src_surface, const Common::Rectangle<u32>& src_rect,
const Surface& dst_surface, const Common::Rectangle<u32>& dst_rect, SurfaceType type) { const Surface& dst_surface, const Common::Rectangle<u32>& dst_rect,
vk::ImageSubresourceLayers image_range{{}, {}, 0, 1}; u32 src_level = 0, u32 dst_level = 0) {
switch (src_surface->type) { vk::ImageSubresourceLayers src_range{{}, src_level, 0, 1};
vk::ImageSubresourceLayers dst_range{{}, dst_level, 0, 1};
auto GetAspect = [](SurfaceType type) -> vk::ImageAspectFlags {
switch (type) {
case SurfaceParams::SurfaceType::Color: case SurfaceParams::SurfaceType::Color:
case SurfaceParams::SurfaceType::Texture: case SurfaceParams::SurfaceType::Texture:
image_range.aspectMask = vk::ImageAspectFlagBits::eColor; return vk::ImageAspectFlagBits::eColor;
break;
case SurfaceParams::SurfaceType::Depth: case SurfaceParams::SurfaceType::Depth:
image_range.aspectMask = vk::ImageAspectFlagBits::eDepth; return vk::ImageAspectFlagBits::eDepth;
break;
case SurfaceParams::SurfaceType::DepthStencil: case SurfaceParams::SurfaceType::DepthStencil:
image_range.aspectMask = return vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
return vk::ImageAspectFlagBits::eNone;
} }
};
src_range.aspectMask = GetAspect(src_surface->type);
dst_range.aspectMask = GetAspect(dst_surface->type);
// Prepare images for transfer // Prepare images for transfer
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer(); auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
@ -276,7 +281,7 @@ static bool BlitTextures(const Surface& src_surface, const Common::Rectangle<u32
vk::Offset3D{static_cast<s32>(dst_rect.right), static_cast<s32>(dst_rect.top), 1} vk::Offset3D{static_cast<s32>(dst_rect.right), static_cast<s32>(dst_rect.top), 1}
}; };
vk::ImageBlit blit_area{image_range, src_offsets, image_range, dst_offsets}; vk::ImageBlit blit_area{src_range, src_offsets, dst_range, dst_offsets};
cmdbuffer.blitImage(src_texture.GetHandle(), vk::ImageLayout::eTransferSrcOptimal, cmdbuffer.blitImage(src_texture.GetHandle(), vk::ImageLayout::eTransferSrcOptimal,
dst_texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal, dst_texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
{blit_area}, vk::Filter::eNearest); {blit_area}, vk::Filter::eNearest);
@ -497,10 +502,8 @@ void RasterizerCacheVulkan::CopySurface(const Surface& src_surface, const Surfac
return; return;
} }
if (src_surface->CanSubRect(subrect_params)) { if (src_surface->CanSubRect(subrect_params)) {
auto srect = src_surface->GetScaledSubRect(subrect_params); BlitTextures(src_surface, src_surface->GetScaledSubRect(subrect_params),
auto drect = dst_surface->GetScaledSubRect(subrect_params); dst_surface, dst_surface->GetScaledSubRect(subrect_params));
BlitTextures(src_surface, srect, dst_surface, drect, src_surface->type);
return; return;
} }
@ -811,7 +814,7 @@ bool RasterizerCacheVulkan::BlitSurfaces(const Surface& src_surface,
return false; return false;
dst_surface->InvalidateAllWatcher(); dst_surface->InvalidateAllWatcher();
return BlitTextures(src_surface, src_rect, dst_surface, dst_rect, src_surface->type); return BlitTextures(src_surface, src_rect, dst_surface, dst_rect);
} }
Surface RasterizerCacheVulkan::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale, Surface RasterizerCacheVulkan::GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
@ -1016,12 +1019,10 @@ Surface RasterizerCacheVulkan::GetTextureSurface(const Pica::Texture::TextureInf
ValidateSurface(level_surface, level_surface->addr, level_surface->size); ValidateSurface(level_surface, level_surface->addr, level_surface->size);
} }
//state.ResetTexture(level_surface->texture.handle);
//state.Apply();
if (!surface->is_custom /*&& texture_filterer->IsNull()*/) { if (!surface->is_custom /*&& texture_filterer->IsNull()*/) {
auto src_rect = level_surface->GetScaledRect(); BlitTextures(level_surface, level_surface->GetScaledRect(),
auto dst_rect = surface_params.GetScaledRect(); surface, surface_params.GetScaledRect(),
BlitSurfaces(level_surface, src_rect, surface, dst_rect); 0, level);
} }
watcher->Validate(); watcher->Validate();
} }

View File

@ -468,7 +468,6 @@ void VulkanState::ApplyRenderState(const Pica::Regs& regs) {
render_pipeline_builder.SetBlendAttachment(att.blendEnable, att.srcColorBlendFactor, att.dstColorBlendFactor, render_pipeline_builder.SetBlendAttachment(att.blendEnable, att.srcColorBlendFactor, att.dstColorBlendFactor,
att.colorBlendOp, att.srcAlphaBlendFactor, att.dstAlphaBlendFactor, att.colorBlendOp, att.srcAlphaBlendFactor, att.dstAlphaBlendFactor,
att.alphaBlendOp, att.colorWriteMask); att.alphaBlendOp, att.colorWriteMask);
std::cout << "New pipeline!\n";
// Cache the resulted pipeline // Cache the resulted pipeline
pipeline = render_pipeline_builder.Build(); pipeline = render_pipeline_builder.Build();
render_pipelines.emplace(render_pipeline_key, vk::UniquePipeline{pipeline}); render_pipelines.emplace(render_pipeline_key, vk::UniquePipeline{pipeline});

View File

@ -101,8 +101,6 @@ void VKTexture::Create(const Info& create_info) {
info.format = vk::Format::eD32SfloatS8Uint; info.format = vk::Format::eD32SfloatS8Uint;
} }
std::cout << "New surface!\n";
// Create the texture // Create the texture
image_size = info.width * info.height * BytesPerPixel(info.format); image_size = info.width * info.height * BytesPerPixel(info.format);
aspect = GetImageAspect(info.format); aspect = GetImageAspect(info.format);
@ -139,6 +137,36 @@ void VKTexture::Create(const Info& create_info) {
view = device.createImageView(view_info); view = device.createImageView(view_info);
} }
void VKTexture::Create(VKTexture& other) {
auto info = other.info;
Create(info);
// Copy the buffer contents
auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer();
Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal);
auto old_layout = other.GetLayout();
other.Transition(cmdbuffer, vk::ImageLayout::eTransferSrcOptimal);
u32 copy_count = 0;
std::array<vk::ImageCopy, 16> copy_regions;
for (u32 i = 0; i < info.levels; i++) {
copy_regions[copy_count++] = vk::ImageCopy{
vk::ImageSubresourceLayers{aspect, i, 0, 1}, {0},
vk::ImageSubresourceLayers{aspect, i, 0, 1}, {0},
{info.width, info.height, 0}
};
}
cmdbuffer.copyImage(other.GetHandle(), vk::ImageLayout::eTransferSrcOptimal,
texture, vk::ImageLayout::eTransferDstOptimal, copy_count,
copy_regions.data());
Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal);
other.Transition(cmdbuffer, old_layout);
}
void VKTexture::Adopt(const Info& create_info, vk::Image image) { void VKTexture::Adopt(const Info& create_info, vk::Image image) {
info = create_info; info = create_info;
image_size = info.width * info.height * BytesPerPixel(info.format); image_size = info.width * info.height * BytesPerPixel(info.format);
@ -167,7 +195,6 @@ void VKTexture::Destroy() {
memory = memory]() { memory = memory]() {
auto device = g_vk_instace->GetDevice(); auto device = g_vk_instace->GetDevice();
if (texture) { if (texture) {
std::cout << "Surface destroyed!\n";
device.destroyImage(texture); device.destroyImage(texture);
device.destroyImageView(view); device.destroyImageView(view);
device.freeMemory(memory); device.freeMemory(memory);

View File

@ -37,6 +37,7 @@ public:
/// Create a new Vulkan texture object /// Create a new Vulkan texture object
void Create(const Info& info); void Create(const Info& info);
void Create(VKTexture& texture);
void Adopt(const Info& info, vk::Image image); void Adopt(const Info& info, vk::Image image);
void Destroy(); void Destroy();
@ -55,10 +56,10 @@ public:
void Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> dst); void Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span<u8> dst);
/// Used to transition the image to an optimal layout during transfers /// Used to transition the image to an optimal layout during transfers
void OverrideImageLayout(vk::ImageLayout new_layout);
void Transition(vk::CommandBuffer cmdbuffer, vk::ImageLayout new_layout); void Transition(vk::CommandBuffer cmdbuffer, vk::ImageLayout new_layout);
void Transition(vk::CommandBuffer cmdbuffer, vk::ImageLayout new_layout, u32 start_level, u32 level_count, void Transition(vk::CommandBuffer cmdbuffer, vk::ImageLayout new_layout, u32 start_level, u32 level_count,
u32 start_layer, u32 layer_count); u32 start_layer, u32 layer_count);
void OverrideImageLayout(vk::ImageLayout new_layout);
private: private:
std::vector<u8> RGBToRGBA(std::span<u8> data); std::vector<u8> RGBToRGBA(std::span<u8> data);