();
+
+ LOG_WARNING(Service_SSL, "(STUBBED) called. option={}", parameters);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
}
void CreateConnection(HLERequestContext& ctx) {
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index a6e87a3583..66db162c5d 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -467,6 +467,10 @@ void BufferCache::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index,
channel_state->written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index;
const auto& launch_desc = kepler_compute->launch_description;
+ if (((launch_desc.const_buffer_enable_mask >> cbuf_index) & 1) == 0) {
+ LOG_WARNING(HW_GPU, "Skipped binding SSBO: cbuf index {} is not enabled", cbuf_index);
+ return;
+ }
ASSERT(((launch_desc.const_buffer_enable_mask >> cbuf_index) & 1) != 0);
const auto& cbufs = launch_desc.const_buffer_config;
@@ -1701,6 +1705,11 @@ template
Binding BufferCache::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
bool is_written) const {
const GPUVAddr gpu_addr = gpu_memory->Read(ssbo_addr);
+
+ if (gpu_addr == 0) {
+ return NULL_BINDING;
+ }
+
const auto size = [&]() {
const bool is_nvn_cbuf = cbuf_index == 0;
// The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size.
@@ -1723,7 +1732,7 @@ Binding BufferCache::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
const std::optional aligned_device_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr);
if (!aligned_device_addr || size == 0) {
- LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
+ LOG_DEBUG(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
return NULL_BINDING;
}
const std::optional device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h
index 08b7790555..6e084cc079 100644
--- a/src/video_core/query_cache/query_cache.h
+++ b/src/video_core/query_cache/query_cache.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -115,8 +118,8 @@ struct QueryCacheBase::QueryCacheBaseImpl {
QueryCacheBaseImpl(QueryCacheBase* owner_, VideoCore::RasterizerInterface& rasterizer_,
Tegra::MaxwellDeviceMemoryManager& device_memory_, RuntimeType& runtime_,
Tegra::GPU& gpu_)
- : owner{owner_}, rasterizer{rasterizer_},
- device_memory{device_memory_}, runtime{runtime_}, gpu{gpu_} {
+ : owner{owner_}, rasterizer{rasterizer_}, device_memory{device_memory_}, runtime{runtime_},
+ gpu{gpu_} {
streamer_mask = 0;
for (size_t i = 0; i < static_cast(QueryType::MaxQueryTypes); i++) {
streamers[i] = runtime.GetStreamerInterface(static_cast(i));
@@ -267,7 +270,11 @@ void QueryCacheBase::CounterReport(GPUVAddr addr, QueryType counter_type
return;
}
if (False(query_base->flags & QueryFlagBits::IsFinalValueSynced)) [[unlikely]] {
- ASSERT(false);
+ LOG_ERROR(HW_GPU,
+ "Query report value not synchronized. Consider increasing GPU accuracy.");
+ if (!is_synced) [[likely]] {
+ impl->pending_unregister.push_back(query_location);
+ }
return;
}
query_base->value += streamer->GetAmendValue();
@@ -370,8 +377,6 @@ void QueryCacheBase::NotifySegment(bool resume) {
if (resume) {
impl->runtime.ResumeHostConditionalRendering();
} else {
- CounterClose(VideoCommon::QueryType::ZPassPixelCount64);
- CounterClose(VideoCommon::QueryType::StreamingByteCount);
impl->runtime.PauseHostConditionalRendering();
}
}
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 0d07e89b6b..ddd29e7acf 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -324,9 +324,8 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) {
size_t ssbo_index{};
for (const auto& desc : info.storage_buffers_descriptors) {
ASSERT(desc.count == 1);
- if (!buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index,
- desc.cbuf_offset, desc.is_written))
- return false;
+ buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index,
+ desc.cbuf_offset, desc.is_written);
++ssbo_index;
}
}
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 44c06eddf3..1f71bc68c6 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -1161,10 +1164,9 @@ struct QueryCacheRuntimeImpl {
StagingBufferPool& staging_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue,
DescriptorPool& descriptor_pool)
- : rasterizer{rasterizer_}, device_memory{device_memory_},
- buffer_cache{buffer_cache_}, device{device_},
- memory_allocator{memory_allocator_}, scheduler{scheduler_}, staging_pool{staging_pool_},
- guest_streamer(0, runtime),
+ : rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_},
+ device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_},
+ staging_pool{staging_pool_}, guest_streamer(0, runtime),
sample_streamer(static_cast(QueryType::ZPassPixelCount64), runtime, rasterizer,
device, scheduler, memory_allocator, compute_pass_descriptor_queue,
descriptor_pool),
@@ -1300,9 +1302,11 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo
if (impl->hcr_is_set) {
if (impl->hcr_setup.buffer == impl->hcr_buffer &&
impl->hcr_setup.offset == impl->hcr_offset) {
- ResumeHostConditionalRendering();
return;
}
+ }
+ bool was_running = impl->is_hcr_running;
+ if (was_running) {
PauseHostConditionalRendering();
}
impl->hcr_setup.buffer = impl->hcr_buffer;
@@ -1310,7 +1314,9 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo
impl->hcr_setup.flags = is_equal ? VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT : 0;
impl->hcr_is_set = true;
impl->is_hcr_running = false;
- ResumeHostConditionalRendering();
+ if (was_running) {
+ ResumeHostConditionalRendering();
+ }
}
void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal) {
@@ -1325,7 +1331,8 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo
to_resolve = buffer->Handle();
to_resolve_offset = static_cast(offset);
}
- if (impl->is_hcr_running) {
+ bool was_running = impl->is_hcr_running;
+ if (was_running) {
PauseHostConditionalRendering();
}
impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve,
@@ -1335,7 +1342,9 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo
impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
impl->hcr_is_set = true;
impl->is_hcr_running = false;
- ResumeHostConditionalRendering();
+ if (was_running) {
+ ResumeHostConditionalRendering();
+ }
}
bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::LookupData object_1,
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 33b90a1ec9..c803b50e24 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -217,8 +217,6 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
FlushWork();
gpu_memory->FlushCaching();
- query_cache.NotifySegment(true);
-
GraphicsPipeline* const pipeline{pipeline_cache.CurrentGraphicsPipeline()};
if (!pipeline) {
return;
@@ -232,9 +230,13 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
UpdateDynamicStates();
HandleTransformFeedback();
+ query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
+
draw_func();
+
+ query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, false);
}
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
@@ -311,8 +313,6 @@ void RasterizerVulkan::DrawTexture() {
};
FlushWork();
- query_cache.NotifySegment(true);
-
std::scoped_lock l{texture_cache.mutex};
texture_cache.SynchronizeGraphicsDescriptors();
texture_cache.UpdateRenderTargets(false);
@@ -359,10 +359,6 @@ void RasterizerVulkan::Clear(u32 layer_count) {
FlushWork();
gpu_memory->FlushCaching();
- query_cache.NotifySegment(true);
- query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
- maxwell3d->regs.zpass_pixel_count_enable);
-
auto& regs = maxwell3d->regs;
const bool use_color = regs.clear_surface.R || regs.clear_surface.G || regs.clear_surface.B ||
regs.clear_surface.A;
@@ -378,6 +374,10 @@ void RasterizerVulkan::Clear(u32 layer_count) {
const VkExtent2D render_area = framebuffer->RenderArea();
scheduler.RequestRenderpass(framebuffer);
+ query_cache.NotifySegment(true);
+ query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
+ maxwell3d->regs.zpass_pixel_count_enable);
+
u32 up_scale = 1;
u32 down_shift = 0;
if (texture_cache.IsRescaling()) {
@@ -518,6 +518,14 @@ void RasterizerVulkan::DispatchCompute() {
}
const std::array dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z};
scheduler.RequestOutsideRenderPassOperationContext();
+ static constexpr VkMemoryBarrier READ_BARRIER{
+ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
+ };
+ scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
+ 0, READ_BARRIER); });
scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); });
}
@@ -824,6 +832,7 @@ std::optional RasterizerVulkan::AccelerateDisplay(
if (!image_view) {
return {};
}
+
query_cache.NotifySegment(false);
const auto& resolution = Settings::values.resolution_info;
@@ -935,22 +944,20 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateDepthBounds(regs);
UpdateStencilFaces(regs);
UpdateLineWidth(regs);
- // TODO: updating line stipple causes the cmdbuf to die
- // UpdateLineStipple(regs);
const u8 dynamic_state = Settings::values.dyna_state.GetValue();
auto features = DynamicFeatures{
- .has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported()
- && dynamic_state > 0,
- .has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported()
- && dynamic_state > 1,
- .has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported()
- && dynamic_state > 1,
- .has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported()
- && dynamic_state > 2,
- .has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported()
- && dynamic_state > 2,
+ .has_extended_dynamic_state =
+ device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0,
+ .has_extended_dynamic_state_2 =
+ device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1,
+ .has_extended_dynamic_state_2_extra =
+ device.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1,
+ .has_extended_dynamic_state_3_blend =
+ device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2,
+ .has_extended_dynamic_state_3_enables =
+ device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2,
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
};
@@ -975,16 +982,12 @@ void RasterizerVulkan::UpdateDynamicStates() {
if (features.has_extended_dynamic_state_3_enables) {
using namespace Tegra::Engines;
- if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE
- || device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
- struct In
- {
+ if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE ||
+ device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
+ struct In {
const Maxwell3D::Regs::VertexAttribute::Type d;
- In(Maxwell3D::Regs::VertexAttribute::Type n)
- : d(n)
- {}
- bool operator()(Maxwell3D::Regs::VertexAttribute n) const
- {
+ In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
+ bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
return n.type == d;
}
};
@@ -1135,36 +1138,36 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
if (is_d24 && !device.SupportsD24DepthBuffer()) {
static constexpr const size_t length = sizeof(NEEDS_D24) / sizeof(NEEDS_D24[0]);
- static constexpr const u64 *start = NEEDS_D24;
- static constexpr const u64 *end = NEEDS_D24 + length;
+ static constexpr const u64* start = NEEDS_D24;
+ static constexpr const u64* end = NEEDS_D24 + length;
- const u64 *it = std::find(start, end, program_id);
+ const u64* it = std::find(start, end, program_id);
if (it != end) {
// the base formulas can be obtained from here:
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
- const double rescale_factor = static_cast(1ULL << (32 - 24))
- / (static_cast(0x1.ep+127));
+ const double rescale_factor =
+ static_cast(1ULL << (32 - 24)) / (static_cast(0x1.ep+127));
units = static_cast(static_cast(units) * rescale_factor);
}
}
- scheduler.Record(
- [constant = units, clamp = regs.depth_bias_clamp, factor = regs.slope_scale_depth_bias, this](
- vk::CommandBuffer cmdbuf) {
- if (device.IsExtDepthBiasControlSupported()) {
- static VkDepthBiasRepresentationInfoEXT bias_info{
- .sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
- .pNext = nullptr,
- .depthBiasRepresentation = VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
- .depthBiasExact = VK_FALSE,
- };
+ scheduler.Record([constant = units, clamp = regs.depth_bias_clamp,
+ factor = regs.slope_scale_depth_bias, this](vk::CommandBuffer cmdbuf) {
+ if (device.IsExtDepthBiasControlSupported()) {
+ static VkDepthBiasRepresentationInfoEXT bias_info{
+ .sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
+ .pNext = nullptr,
+ .depthBiasRepresentation =
+ VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT,
+ .depthBiasExact = VK_FALSE,
+ };
- cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info);
- } else {
- cmdbuf.SetDepthBias(constant, clamp, factor);
- }
- });
+ cmdbuf.SetDepthBias(constant, clamp, factor, &bias_info);
+ } else {
+ cmdbuf.SetDepthBias(constant, clamp, factor);
+ }
+ });
}
void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs) {
@@ -1346,8 +1349,7 @@ void RasterizerVulkan::UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::
});
}
-void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs)
-{
+void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchConservativeRasterizationMode()) {
return;
}
@@ -1359,8 +1361,7 @@ void RasterizerVulkan::UpdateConservativeRasterizationMode(Tegra::Engines::Maxwe
});
}
-void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs)
-{
+void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchLineStippleEnable()) {
return;
}
@@ -1370,19 +1371,7 @@ void RasterizerVulkan::UpdateLineStippleEnable(Tegra::Engines::Maxwell3D::Regs&
});
}
-void RasterizerVulkan::UpdateLineStipple(Tegra::Engines::Maxwell3D::Regs& regs)
-{
- if (!state_tracker.TouchLineStipple()) {
- return;
- }
-
- scheduler.Record([params = regs.line_stipple_params](vk::CommandBuffer cmdbuf) {
- cmdbuf.SetLineStippleEXT(params.factor, static_cast(params.pattern));
- });
-}
-
-void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs)
-{
+void RasterizerVulkan::UpdateLineRasterizationMode(Tegra::Engines::Maxwell3D::Regs& regs) {
// if (!state_tracker.TouchLi()) {
// return;
// }
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 146923db4d..7c556588b6 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -257,16 +260,6 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
void Scheduler::AllocateNewContext() {
// Enable counters once again. These are disabled when a command buffer is finished.
- if (query_cache) {
-#if ANDROID
- if (Settings::IsGPULevelHigh()) {
- // This is problematic on Android, disable on GPU Normal.
- query_cache->NotifySegment(true);
- }
-#else
- query_cache->NotifySegment(true);
-#endif
- }
}
void Scheduler::InvalidateState() {
@@ -276,15 +269,7 @@ void Scheduler::InvalidateState() {
}
void Scheduler::EndPendingOperations() {
-#if ANDROID
- if (Settings::IsGPULevelHigh()) {
- // This is problematic on Android, disable on GPU Normal.
- // query_cache->DisableStreams();
- }
-#else
- // query_cache->DisableStreams();
-#endif
- query_cache->NotifySegment(false);
+ query_cache->CounterReset(VideoCommon::QueryType::ZPassPixelCount64);
EndRenderPass();
}
@@ -292,6 +277,10 @@ void Scheduler::EndRenderPass() {
if (!state.renderpass) {
return;
}
+
+ query_cache->CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, false);
+ query_cache->NotifySegment(false);
+
Record([num_images = num_renderpass_images, images = renderpass_images,
ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) {
std::array barriers;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 022a9ae572..9259639107 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -38,6 +41,7 @@ using VideoCommon::ImageInfo;
using VideoCommon::ImageType;
using VideoCommon::SubresourceRange;
using VideoCore::Surface::BytesPerBlock;
+using VideoCore::Surface::HasAlpha;
using VideoCore::Surface::IsPixelFormatASTC;
using VideoCore::Surface::IsPixelFormatInteger;
using VideoCore::Surface::SurfaceType;
@@ -1323,6 +1327,7 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
case PixelFormat::D32_FLOAT_S8_UINT:
case PixelFormat::Invalid:
default:
+ LOG_ERROR(Render_Vulkan, "Unimplemented texture conversion from {} to {} format type", src_view.format, dst_view.format);
break;
}
}
@@ -1364,6 +1369,16 @@ bool TextureCacheRuntime::IsFormatScalable(PixelFormat format) {
void TextureCacheRuntime::CopyImage(Image& dst, Image& src,
std::span copies) {
+ // As per the size-compatible formats section of vulkan, copy manually via ReinterpretImage
+ // these images that aren't size-compatible
+ if (BytesPerBlock(src.info.format) != BytesPerBlock(dst.info.format)) {
+ auto oneCopy = VideoCommon::ImageCopy{
+ .src_offset = VideoCommon::Offset3D(0, 0, 0),
+ .dst_offset = VideoCommon::Offset3D(0, 0, 0),
+ .extent = dst.info.size
+ };
+ return ReinterpretImage(dst, src, std::span{&oneCopy, 1});
+ }
boost::container::small_vector vk_copies(copies.size());
const VkImageAspectFlags aspect_mask = dst.AspectMask();
ASSERT(aspect_mask == src.AspectMask());
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 9055b1b929..1998849e84 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -237,6 +240,44 @@ SurfaceType GetFormatType(PixelFormat pixel_format) {
return SurfaceType::Invalid;
}
+bool HasAlpha(PixelFormat pixel_format) {
+ switch (pixel_format) {
+ case PixelFormat::A8B8G8R8_UNORM:
+ case PixelFormat::A8B8G8R8_SNORM:
+ case PixelFormat::A8B8G8R8_SINT:
+ case PixelFormat::A8B8G8R8_UINT:
+ case PixelFormat::A1R5G5B5_UNORM:
+ case PixelFormat::A2B10G10R10_UNORM:
+ case PixelFormat::A2B10G10R10_UINT:
+ case PixelFormat::A2R10G10B10_UNORM:
+ case PixelFormat::A1B5G5R5_UNORM:
+ case PixelFormat::A5B5G5R1_UNORM:
+ case PixelFormat::R16G16B16A16_FLOAT:
+ case PixelFormat::R16G16B16A16_UNORM:
+ case PixelFormat::R16G16B16A16_SNORM:
+ case PixelFormat::R16G16B16A16_SINT:
+ case PixelFormat::R16G16B16A16_UINT:
+ case PixelFormat::R32G32B32A32_UINT:
+ case PixelFormat::BC1_RGBA_UNORM:
+ case PixelFormat::B8G8R8A8_UNORM:
+ case PixelFormat::R32G32B32A32_FLOAT:
+ case PixelFormat::R32G32B32A32_SINT:
+ case PixelFormat::A8B8G8R8_SRGB:
+ case PixelFormat::B8G8R8A8_SRGB:
+ case PixelFormat::BC1_RGBA_SRGB:
+ case PixelFormat::A4B4G4R4_UNORM:
+ case PixelFormat::BC2_SRGB:
+ case PixelFormat::BC2_UNORM:
+ case PixelFormat::BC3_SRGB:
+ case PixelFormat::BC3_UNORM:
+ case PixelFormat::BC7_SRGB:
+ case PixelFormat::BC7_UNORM:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool IsPixelFormatASTC(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4_UNORM:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index ec9cd2fbf0..4ccb24f27d 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -503,6 +503,8 @@ PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format);
SurfaceType GetFormatType(PixelFormat pixel_format);
+bool HasAlpha(PixelFormat pixel_format);
+
bool IsPixelFormatASTC(PixelFormat format);
bool IsPixelFormatBCn(PixelFormat format);
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 40136c3fbf..5d80531b47 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -11,8 +11,8 @@
#include
#include "common/common_types.h"
-#include "common/settings.h"
#include "common/logging/log.h"
+#include "common/settings.h"
#include "video_core/vulkan_common/vk_enum_string_helper.h"
#include "video_core/vulkan_common/vma.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -440,14 +440,15 @@ Instance Instance::Create(u32 version, Span layers, Span