// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include #include #include "common/slot_vector.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_runtime.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scene_resolution.h" #include "video_core/texture_cache/blit_helper.h" #include int main() { using namespace Vulkan; Instance instance(0, false); static vk::detail::DynamicLoader loader; vk::detail::DispatchLoaderDynamic dispatch; dispatch.init( loader.getProcAddress("vkGetInstanceProcAddr")); dispatch.init(instance.GetInstance()); dispatch.init(instance.GetDevice()); Scheduler scheduler(instance); Runtime runtime(instance, scheduler); Common::SlotVector views; Common::SlotVector images; VideoCore::ImageInfo ci; ci.size = {1920,1080,1}; ci.resources = {1,1}; ci.type = AmdGpu::ImageType::Color2D; ci.pixel_format = vk::Format::eR8G8B8A8Unorm; ci.num_bits = 32; const auto color_id = images.insert(instance, runtime, views, ci); auto di = ci; di.pixel_format = vk::Format::eD32SfloatS8Uint; di.props.is_depth = di.props.has_stencil = true; const auto depth_id = images.insert(instance, runtime, views, di); SceneTargets targets(instance, scheduler, runtime, [&](VideoCore::ImageId id, u64 uid) { if (!images.is_allocated(id)) return static_cast(nullptr); return !uid || images[id].image_uid == uid ? &images[id] : nullptr; }); const bool color_eligible = targets.Eligible(images[color_id]); const bool depth_eligible = targets.Eligible(images[depth_id]); std::fprintf(stderr, "Scene target eligibility: color=%d depth=%d\n", color_eligible, depth_eligible); if (!color_eligible || !depth_eligible) { std::puts("Scene targets: unsupported format blit, live scaling skipped"); return 0; } VkBuffer readback{}; VmaAllocation allocation{}; VmaAllocationInfo ai{}; const VkBufferCreateInfo bi{.sType=VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size=16, .usage=VK_BUFFER_USAGE_TRANSFER_DST_BIT}; const VmaAllocationCreateInfo ac{.flags=VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, .usage=VMA_MEMORY_USAGE_AUTO, .requiredFlags=VK_MEMORY_PROPERTY_HOST_COHERENT_BIT}; assert(vmaCreateBuffer(instance.GetAllocator(), &bi, &ac, &readback, &allocation, &ai) == VK_SUCCESS); const auto pixel = [&](vk::Image image, vk::ImageAspectFlagBits aspect, vk::ImageLayout layout, u32 w, u32 h) { scheduler.EndRendering(); const auto cmd = scheduler.CommandBuffer(); const vk::MemoryBarrier2 b{.srcStageMask=vk::PipelineStageFlagBits2::eAllCommands, .srcAccessMask=vk::AccessFlagBits2::eMemoryWrite, .dstStageMask=vk::PipelineStageFlagBits2::eTransfer, .dstAccessMask=vk::AccessFlagBits2::eTransferRead}; cmd.pipelineBarrier2({.memoryBarrierCount=1,.pMemoryBarriers=&b}, dispatch); const vk::BufferImageCopy region{.imageSubresource={aspect,0,0,1}, .imageOffset={s32(w-1),s32(h-1),0},.imageExtent={1,1,1}}; cmd.copyImageToBuffer(image, layout, readback, region, dispatch); const vk::MemoryBarrier2 host{.srcStageMask=vk::PipelineStageFlagBits2::eTransfer, .srcAccessMask=vk::AccessFlagBits2::eTransferWrite, .dstStageMask=vk::PipelineStageFlagBits2::eHost, .dstAccessMask=vk::AccessFlagBits2::eHostRead}; cmd.pipelineBarrier2({.memoryBarrierCount=1,.pMemoryBarriers=&host}, dispatch); scheduler.Finish(); u32 result; std::memcpy(&result, ai.pMappedData, 4); return result; }; VideoCore::ImageViewInfo cv; cv.format = ci.pixel_format; auto dv = cv; dv.format = di.pixel_format; const auto clear_native = [&](u32 rgba, u32 stencil = 7) { vk::ClearValue clear{}; clear.color.float32 = std::array{float(rgba & 255)/255, float((rgba>>8)&255)/255, float((rgba>>16)&255)/255, 1.0f}; runtime.ClearImage(&images[color_id], {}, clear); runtime.Transit(&images[depth_id], vk::ImageLayout::eTransferDstOptimal, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferWrite); runtime.FlushBarriers(); scheduler.CommandBuffer().clearDepthStencilImage( images[depth_id].GetImage(), vk::ImageLayout::eTransferDstOptimal, vk::ClearDepthStencilValue{0.25f, stencil}, vk::ImageSubresourceRange{vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil, 0, 1, 0, 1}, dispatch); }; constexpr u32 stencil_values[] = {0, 255, 128, 85, 170, 7, 13, 255, 0, 170}; u32 stencil_index = 0; for (int preset : {3,1,4,2,0,3,0,1,0,3}) { const u32 copied_stencil = stencil_values[stencil_index++]; const u32 resolved_stencil = copied_stencil ^ 255; const auto output = stencil_index > 7 ? SceneResolution::Size{3840,2160} : SceneResolution::Size{}; const auto size = SceneResolution::ForPreset(preset, output); targets.SetSize(size); assert(SceneResolution::Unpack(SceneResolution::Pack(size)) == size); clear_native(0xffff0000, copied_stencil); auto c = targets.Read(color_id,cv); assert(pixel(c.image,vk::ImageAspectFlagBits::eColor,c.layout,size.width,size.height)==0xffff0000); auto d = targets.Read(depth_id,dv); const u32 depth_bits = pixel(d.image,vk::ImageAspectFlagBits::eDepth,d.layout,size.width,size.height); assert(std::bit_cast(depth_bits)==0.25f); assert((pixel(d.image,vk::ImageAspectFlagBits::eStencil,d.layout,size.width,size.height)&255)==copied_stencil); c = targets.Attachment(color_id,cv); d = targets.Attachment(depth_id,dv); auto cmd = scheduler.CommandBuffer(); const vk::RenderingAttachmentInfo color{.imageView=c.view,.imageLayout=c.layout, .loadOp=vk::AttachmentLoadOp::eClear,.storeOp=vk::AttachmentStoreOp::eStore, .clearValue=vk::ClearValue{.color={.float32=std::array{1.f,0.f,0.f,1.f}}}}; const vk::RenderingAttachmentInfo depth{.imageView=d.view,.imageLayout=d.layout, .loadOp=vk::AttachmentLoadOp::eClear,.storeOp=vk::AttachmentStoreOp::eStore, .clearValue=vk::ClearValue{.depthStencil={0.75f,resolved_stencil}}}; cmd.beginRendering({.renderArea={{0,0},{size.width,size.height}},.layerCount=1, .colorAttachmentCount=1,.pColorAttachments=&color, .pDepthAttachment=&depth,.pStencilAttachment=&depth}, dispatch); cmd.endRendering(dispatch); runtime.Transit(&images[color_id],vk::ImageLayout::eTransferSrcOptimal, vk::PipelineStageFlagBits2::eTransfer,vk::AccessFlagBits2::eTransferRead); runtime.FlushBarriers(); assert(pixel(images[color_id].GetImage(),vk::ImageAspectFlagBits::eColor, vk::ImageLayout::eTransferSrcOptimal,1920,1080)==0xff0000ff); runtime.Transit(&images[depth_id],vk::ImageLayout::eTransferSrcOptimal, vk::PipelineStageFlagBits2::eTransfer,vk::AccessFlagBits2::eTransferRead); runtime.FlushBarriers(); assert(std::bit_cast(pixel(images[depth_id].GetImage(),vk::ImageAspectFlagBits::eDepth, vk::ImageLayout::eTransferSrcOptimal,1920,1080))==0.75f); assert((pixel(images[depth_id].GetImage(),vk::ImageAspectFlagBits::eStencil, vk::ImageLayout::eTransferSrcOptimal,1920,1080)&255)==resolved_stencil); clear_native(0xff00ff00); c=targets.Read(color_id,cv); assert(pixel(c.image,vk::ImageAspectFlagBits::eColor,c.layout,size.width,size.height)==0xff00ff00); std::printf("Scene targets: %ux%u color/depth/stencil roundtrip PASS\n",size.width,size.height); } auto hi = ci; hi.size = {960, 540, 1}; hi.resources = {4, 1}; const auto half_id = images.insert(instance, runtime, views, hi); const auto scene = SceneResolution::ForPreset(4); targets.SetSize(scene); auto& half = images[half_id]; assert(targets.Eligible(half) && !targets.EligibleScene(half)); assert((targets.ProxySize(half, 0) == SceneResolution::Size{320, 180})); assert((targets.ProxySize(half, 2) == SceneResolution::Size{80, 45})); auto lv = cv; lv.range.base.level = 2; const auto proxy = targets.Attachment(half_id, lv); { auto cmd = scheduler.CommandBuffer(); const vk::RenderingAttachmentInfo color{.imageView=proxy.view,.imageLayout=proxy.layout, .loadOp=vk::AttachmentLoadOp::eClear,.storeOp=vk::AttachmentStoreOp::eStore, .clearValue=vk::ClearValue{.color={.float32=std::array{0.f,1.f,0.f,1.f}}}}; cmd.beginRendering({.renderArea={{0,0},{80,45}},.layerCount=1, .colorAttachmentCount=1,.pColorAttachments=&color}, dispatch); cmd.endRendering(dispatch); } assert(targets.ProxyCurrent(half, 2, 1) && !targets.ProxyCurrent(half, 0, 4)); runtime.Transit(&half, vk::ImageLayout::eTransferSrcOptimal, vk::PipelineStageFlagBits2::eTransfer, vk::AccessFlagBits2::eTransferRead); runtime.FlushBarriers(); { scheduler.EndRendering(); const auto cmd = scheduler.CommandBuffer(); const vk::MemoryBarrier2 b{.srcStageMask=vk::PipelineStageFlagBits2::eAllCommands, .srcAccessMask=vk::AccessFlagBits2::eMemoryWrite, .dstStageMask=vk::PipelineStageFlagBits2::eTransfer, .dstAccessMask=vk::AccessFlagBits2::eTransferRead}; cmd.pipelineBarrier2({.memoryBarrierCount=1,.pMemoryBarriers=&b}, dispatch); const vk::BufferImageCopy region{.imageSubresource={vk::ImageAspectFlagBits::eColor,2,0,1}, .imageOffset={239,134,0},.imageExtent={1,1,1}}; cmd.copyImageToBuffer(half.GetImage(), vk::ImageLayout::eTransferSrcOptimal, readback, region, dispatch); scheduler.Finish(); u32 result; std::memcpy(&result, ai.pMappedData, 4); assert(result == 0xff00ff00); } std::puts("Scene targets: half-resolution mip level proxy roundtrip PASS"); const std::size_t before_release = targets.ProxyCount(); assert(before_release >= 1); targets.Forget(images[half_id].image_uid); const auto again_id = images.insert(instance, runtime, views, ci); targets.Attachment(again_id, cv); assert(targets.ProxyCount() == before_release); assert(!targets.Tracks(images[half_id].image_uid)); targets.Forget(images[again_id].image_uid); targets.Forget(images[color_id].image_uid); targets.Attachment(depth_id, dv); assert(targets.ProxyCount() == before_release - 2); scheduler.Finish(); std::puts("Scene targets: proxies of freed images released PASS"); scheduler.Finish(); vmaDestroyBuffer(instance.GetAllocator(),readback,allocation); }