renderer_vulkan: Fix mipmap generation
This commit is contained in:
@ -328,6 +328,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
}
|
||||
|
||||
// Sync and bind the texture surfaces
|
||||
VKTexture temp_tex;
|
||||
const auto pica_textures = regs.texturing.GetTextures();
|
||||
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++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);
|
||||
Surface surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface != nullptr) {
|
||||
state.SetTexture(texture_index, surface->texture);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
@ -1141,6 +1151,9 @@ void RasterizerVulkan::SyncCullMode() {
|
||||
const auto& regs = Pica::g_state.regs;
|
||||
|
||||
auto& state = VulkanState::Get();
|
||||
state.SetCullMode(vk::CullModeFlagBits::eNone);
|
||||
return;
|
||||
|
||||
switch (regs.rasterizer.cull_mode) {
|
||||
case Pica::RasterizerRegs::CullMode::KeepAll:
|
||||
state.SetCullMode(vk::CullModeFlagBits::eNone);
|
||||
|
@ -239,23 +239,28 @@ inline vk::ImageSubresourceRange SubResourceLayersToRange(const vk::ImageSubreso
|
||||
}
|
||||
|
||||
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) {
|
||||
vk::ImageSubresourceLayers image_range{{}, {}, 0, 1};
|
||||
switch (src_surface->type) {
|
||||
case SurfaceParams::SurfaceType::Color:
|
||||
case SurfaceParams::SurfaceType::Texture:
|
||||
image_range.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
break;
|
||||
case SurfaceParams::SurfaceType::Depth:
|
||||
image_range.aspectMask = vk::ImageAspectFlagBits::eDepth;
|
||||
break;
|
||||
case SurfaceParams::SurfaceType::DepthStencil:
|
||||
image_range.aspectMask =
|
||||
vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
const Surface& dst_surface, const Common::Rectangle<u32>& dst_rect,
|
||||
u32 src_level = 0, u32 dst_level = 0) {
|
||||
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::Texture:
|
||||
return vk::ImageAspectFlagBits::eColor;
|
||||
case SurfaceParams::SurfaceType::Depth:
|
||||
return vk::ImageAspectFlagBits::eDepth;
|
||||
case SurfaceParams::SurfaceType::DepthStencil:
|
||||
return vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
return vk::ImageAspectFlagBits::eNone;
|
||||
}
|
||||
};
|
||||
|
||||
src_range.aspectMask = GetAspect(src_surface->type);
|
||||
dst_range.aspectMask = GetAspect(dst_surface->type);
|
||||
|
||||
// Prepare images for transfer
|
||||
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::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,
|
||||
dst_texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal,
|
||||
{blit_area}, vk::Filter::eNearest);
|
||||
@ -497,10 +502,8 @@ void RasterizerCacheVulkan::CopySurface(const Surface& src_surface, const Surfac
|
||||
return;
|
||||
}
|
||||
if (src_surface->CanSubRect(subrect_params)) {
|
||||
auto srect = src_surface->GetScaledSubRect(subrect_params);
|
||||
auto drect = dst_surface->GetScaledSubRect(subrect_params);
|
||||
|
||||
BlitTextures(src_surface, srect, dst_surface, drect, src_surface->type);
|
||||
BlitTextures(src_surface, src_surface->GetScaledSubRect(subrect_params),
|
||||
dst_surface, dst_surface->GetScaledSubRect(subrect_params));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -811,7 +814,7 @@ bool RasterizerCacheVulkan::BlitSurfaces(const Surface& src_surface,
|
||||
return false;
|
||||
|
||||
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,
|
||||
@ -1016,12 +1019,10 @@ Surface RasterizerCacheVulkan::GetTextureSurface(const Pica::Texture::TextureInf
|
||||
ValidateSurface(level_surface, level_surface->addr, level_surface->size);
|
||||
}
|
||||
|
||||
//state.ResetTexture(level_surface->texture.handle);
|
||||
//state.Apply();
|
||||
if (!surface->is_custom /*&& texture_filterer->IsNull()*/) {
|
||||
auto src_rect = level_surface->GetScaledRect();
|
||||
auto dst_rect = surface_params.GetScaledRect();
|
||||
BlitSurfaces(level_surface, src_rect, surface, dst_rect);
|
||||
BlitTextures(level_surface, level_surface->GetScaledRect(),
|
||||
surface, surface_params.GetScaledRect(),
|
||||
0, level);
|
||||
}
|
||||
watcher->Validate();
|
||||
}
|
||||
|
@ -468,7 +468,6 @@ void VulkanState::ApplyRenderState(const Pica::Regs& regs) {
|
||||
render_pipeline_builder.SetBlendAttachment(att.blendEnable, att.srcColorBlendFactor, att.dstColorBlendFactor,
|
||||
att.colorBlendOp, att.srcAlphaBlendFactor, att.dstAlphaBlendFactor,
|
||||
att.alphaBlendOp, att.colorWriteMask);
|
||||
std::cout << "New pipeline!\n";
|
||||
// Cache the resulted pipeline
|
||||
pipeline = render_pipeline_builder.Build();
|
||||
render_pipelines.emplace(render_pipeline_key, vk::UniquePipeline{pipeline});
|
||||
|
@ -101,8 +101,6 @@ void VKTexture::Create(const Info& create_info) {
|
||||
info.format = vk::Format::eD32SfloatS8Uint;
|
||||
}
|
||||
|
||||
std::cout << "New surface!\n";
|
||||
|
||||
// Create the texture
|
||||
image_size = info.width * info.height * BytesPerPixel(info.format);
|
||||
aspect = GetImageAspect(info.format);
|
||||
@ -139,6 +137,36 @@ void VKTexture::Create(const Info& create_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) {
|
||||
info = create_info;
|
||||
image_size = info.width * info.height * BytesPerPixel(info.format);
|
||||
@ -167,7 +195,6 @@ void VKTexture::Destroy() {
|
||||
memory = memory]() {
|
||||
auto device = g_vk_instace->GetDevice();
|
||||
if (texture) {
|
||||
std::cout << "Surface destroyed!\n";
|
||||
device.destroyImage(texture);
|
||||
device.destroyImageView(view);
|
||||
device.freeMemory(memory);
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
|
||||
/// Create a new Vulkan texture object
|
||||
void Create(const Info& info);
|
||||
void Create(VKTexture& texture);
|
||||
void Adopt(const Info& info, vk::Image image);
|
||||
void Destroy();
|
||||
|
||||
@ -55,10 +56,10 @@ public:
|
||||
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
|
||||
void OverrideImageLayout(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,
|
||||
u32 start_layer, u32 layer_count);
|
||||
void OverrideImageLayout(vk::ImageLayout new_layout);
|
||||
|
||||
private:
|
||||
std::vector<u8> RGBToRGBA(std::span<u8> data);
|
||||
|
Reference in New Issue
Block a user