mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 04:04:18 +02:00
Added foveated rendering for the Quest
- Eyes render under a VK_EXT_fragment_density_map: full rate around each eye's forward direction, 2x2 then 4x4 pixel blocks towards the edges ([vr] foveation = off|low|medium|high, default off). XR_FB_foveation cannot help here: the runtime's maps only shape passes drawing into its swapchain, and the eyes reach it through a copy. - aurora-main/patches/dawn/aurora_fdm.inc: Dawn enables the extension only on request and for dynamic rendering, flags every render pipeline, and chains an immutable RG8 map into any pass whose first color attachment is a view bound to one (ABI: include/aurora/dawn_fdm_abi.h). - android/Build-QuestDawn.ps1 builds the pinned Dawn revision with those patches for arm64 (dawn-build CI flags, protobuf off) into a cached package; Build-Quest.ps1 links it (-StockDawn opts out) and AuroraDawnProvider.cmake enables the ABI from its manifest. - lib/gfx/foveation.hpp generates the maps (32 px per texel, densities 255/127/63); an eye is foveated only when single_pass_eyes draws it in one render pass. Menus never are. - Live level from the headset panel's VR tab and the launcher; the launch decides whether the device has maps. debug.wiicompiled.foveation and debug.wiicompiled.fdm for A/B. - Tests: Foveation cases in gx_fifo_tests, mkw_vr_config_tests. Docs: OPENXR.md, quest-port.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ceeeba0332
commit
cc653272c8
30 files changed
+1527
-14
No files matched your search
@@ -1,13 +1,47 @@
|
||||
"""Apply the versioned Aurora native Vulkan ABI to the pinned Dawn source tree."""
|
||||
"""Apply Aurora's native Dawn patches to the pinned Dawn source tree.
|
||||
|
||||
- The versioned native Vulkan ABI (aurora_vulkan_interop.inc): the OpenXR runtime creates Dawn's
|
||||
Vulkan instance and device, used by the Windows Vulkan OpenXR binding.
|
||||
- Fragment density maps (aurora_fdm.inc): VK_EXT_fragment_density_map on Aurora's immersive eye
|
||||
render passes, used by the Quest build.
|
||||
|
||||
Every edit is anchored on Dawn source text, asserts that the anchor exists, and is skipped when it
|
||||
is already applied, so the script can run again on a patched tree.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import shutil, sys
|
||||
root = Path(sys.argv[1]).resolve()
|
||||
here = Path(__file__).resolve().parent
|
||||
for name in ("aurora_vulkan_hooks.h", "aurora_vulkan_interop.inc"):
|
||||
shutil.copyfile(here / name, root / "src/dawn/native/vulkan" / name)
|
||||
shutil.copyfile(here.parent.parent / "include/aurora/dawn_vulkan_abi.h",
|
||||
root / "src/dawn/native/vulkan/aurora_dawn_vulkan_abi.h")
|
||||
p = root / "src/dawn/native/vulkan/VulkanBackend.cpp"
|
||||
vulkan = root / "src/dawn/native/vulkan"
|
||||
include = here.parent.parent / "include/aurora"
|
||||
|
||||
|
||||
def read(path):
|
||||
with open(path, encoding="utf-8", newline="") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def write(path, text):
|
||||
with open(path, "w", encoding="utf-8", newline="") as f:
|
||||
f.write(text)
|
||||
|
||||
|
||||
def insert(path, anchor, text, after=True):
|
||||
"""Insert `text` after (or before) the one occurrence of `anchor` unless it is already there."""
|
||||
s = read(path)
|
||||
if text in s:
|
||||
return
|
||||
assert s.count(anchor) == 1, f"{path.name}: expected one occurrence of {anchor!r}"
|
||||
s = s.replace(anchor, anchor + text if after else text + anchor, 1)
|
||||
write(path, s)
|
||||
|
||||
|
||||
for name in ("aurora_vulkan_hooks.h", "aurora_vulkan_interop.inc", "aurora_fdm.h", "aurora_fdm.inc"):
|
||||
shutil.copyfile(here / name, vulkan / name)
|
||||
shutil.copyfile(include / "dawn_vulkan_abi.h", vulkan / "aurora_dawn_vulkan_abi.h")
|
||||
shutil.copyfile(include / "dawn_fdm_abi.h", vulkan / "aurora_dawn_fdm_abi.h")
|
||||
|
||||
p = vulkan / "VulkanBackend.cpp"
|
||||
s = p.read_text()
|
||||
if '#include "aurora_vulkan_interop.inc"' not in s:
|
||||
s += '\n#include "aurora_vulkan_interop.inc"\n'
|
||||
@@ -23,7 +57,7 @@ new = '''((filename.size() > 2 && filename[1] == ':') || filename.starts_with("\
|
||||
if old in s:
|
||||
s = s.replace(old, new, 1)
|
||||
p.write_text(s)
|
||||
p = root / "src/dawn/native/vulkan/VulkanFunctions.cpp"
|
||||
p = vulkan / "VulkanFunctions.cpp"
|
||||
s = p.read_text()
|
||||
if '#include "aurora_vulkan_hooks.h"' not in s:
|
||||
marker = 'namespace dawn::native::vulkan {'
|
||||
@@ -36,3 +70,86 @@ if '#include "aurora_vulkan_hooks.h"' not in s:
|
||||
assert old in s, old
|
||||
s = s.replace(old, old + '\n ' + new, 1)
|
||||
p.write_text(s)
|
||||
|
||||
# Fragment density maps.
|
||||
p = vulkan / "VulkanBackend.cpp"
|
||||
s = read(p)
|
||||
if '#include "aurora_fdm.inc"' not in s:
|
||||
write(p, s + '#include "aurora_fdm.inc"\n')
|
||||
|
||||
insert(vulkan / "VulkanExtensions.h", " RasterizationOrderAttachmentAccess,\n",
|
||||
" FragmentDensityMap,\n")
|
||||
insert(vulkan / "VulkanExtensions.cpp",
|
||||
' {DeviceExt::RasterizationOrderAttachmentAccess, "VK_EXT_rasterization_order_attachment_access"},\n',
|
||||
' {DeviceExt::FragmentDensityMap, "VK_EXT_fragment_density_map"},\n')
|
||||
insert(vulkan / "VulkanExtensions.cpp", " case DeviceExt::EnumCount:\n",
|
||||
" // Aurora only attaches maps to dynamic rendering passes (aurora_fdm.inc).\n"
|
||||
" case DeviceExt::FragmentDensityMap:\n"
|
||||
" hasDependencies = HasDep(DeviceExt::DynamicRendering);\n"
|
||||
" break;\n\n",
|
||||
after=False)
|
||||
|
||||
insert(vulkan / "VulkanInfo.h", " VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extendedDynamicStateFeatures;\n",
|
||||
" VkPhysicalDeviceFragmentDensityMapFeaturesEXT fragmentDensityMapFeatures;\n")
|
||||
insert(vulkan / "VulkanInfo.h", " VkPhysicalDeviceDrmPropertiesEXT drmProperties;\n",
|
||||
" VkPhysicalDeviceFragmentDensityMapPropertiesEXT fragmentDensityMapProperties;\n")
|
||||
insert(vulkan / "VulkanInfo.cpp",
|
||||
" // Use vkGetPhysicalDevice{Features,Properties}2 if required to gather information about\n",
|
||||
" if (info.extensions[DeviceExt::FragmentDensityMap]) {\n"
|
||||
" featuresChain.Add(&info.fragmentDensityMapFeatures,\n"
|
||||
" VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT);\n"
|
||||
" propertiesChain.Add(&info.fragmentDensityMapProperties,\n"
|
||||
" VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT);\n"
|
||||
" }\n\n",
|
||||
after=False)
|
||||
|
||||
insert(vulkan / "DeviceVk.cpp", '#include "src/dawn/native/vulkan/DeviceVk.h"\n',
|
||||
'#include "aurora_fdm.h"\n')
|
||||
insert(vulkan / "DeviceVk.cpp", " usedKnobs.extensions = mDeviceInfo.extensions;\n",
|
||||
" // Aurora: fragment density maps for its immersive eye passes, only on request\n"
|
||||
" // (aurora_fdm.inc).\n"
|
||||
" if (!AuroraFdmWanted(mDeviceInfo, IsToggleEnabled(Toggle::VulkanUseDynamicRendering) &&\n"
|
||||
" !HasFeature(Feature::DawnLoadResolveTexture))) {\n"
|
||||
" usedKnobs.extensions.set(DeviceExt::FragmentDensityMap, false);\n"
|
||||
" }\n")
|
||||
insert(vulkan / "DeviceVk.cpp",
|
||||
" usedKnobs.extendedDynamicStateFeatures = mDeviceInfo.extendedDynamicStateFeatures;\n"
|
||||
" featuresChain.Add(&usedKnobs.extendedDynamicStateFeatures);\n"
|
||||
" }\n",
|
||||
"\n"
|
||||
" if (usedKnobs.HasExt(DeviceExt::FragmentDensityMap)) {\n"
|
||||
" usedKnobs.fragmentDensityMapFeatures = mDeviceInfo.fragmentDensityMapFeatures;\n"
|
||||
" // Aurora's maps are immutable, read when a render pass is recorded.\n"
|
||||
" usedKnobs.fragmentDensityMapFeatures.fragmentDensityMapDynamic = VK_FALSE;\n"
|
||||
" featuresChain.Add(&usedKnobs.fragmentDensityMapFeatures);\n"
|
||||
" }\n")
|
||||
insert(vulkan / "DeviceVk.cpp", " mStaticSamplerCache.clear();\n",
|
||||
"\n AuroraFdmDestroy(this);\n")
|
||||
|
||||
insert(vulkan / "RenderPipelineVk.cpp", " createInfo.renderPass = nullRenderPass;\n",
|
||||
"\n"
|
||||
" // Aurora: any pipeline may draw in an eye pass with a fragment density map\n"
|
||||
" // (aurora_fdm.inc).\n"
|
||||
" if (device->GetDeviceInfo().HasExt(DeviceExt::FragmentDensityMap)) {\n"
|
||||
" createInfo.flags |= VK_PIPELINE_CREATE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT;\n"
|
||||
" }\n")
|
||||
|
||||
insert(vulkan / "CommandBufferVk.cpp", '#include "src/dawn/native/vulkan/CommandBufferVk.h"\n',
|
||||
'#include "aurora_fdm.h"\n')
|
||||
insert(vulkan / "CommandBufferVk.cpp", " // TODO(crbug.com/463893794): Handle ExpandResolveTexture.\n",
|
||||
" // Aurora: the fragment density map bound to the first color attachment's view\n"
|
||||
" // (aurora_fdm.inc).\n"
|
||||
" VkRenderingFragmentDensityMapAttachmentInfoEXT fragmentDensityMap;\n"
|
||||
" if (attachmentMask[ColorAttachmentIndex(uint8_t(0))]) {\n"
|
||||
" const ::VkImageView densityMap = AuroraFdmViewFor(\n"
|
||||
" device, renderPass->colorAttachments[ColorAttachmentIndex(uint8_t(0))].view.Get());\n"
|
||||
" if (densityMap != VK_NULL_HANDLE) {\n"
|
||||
" fragmentDensityMap.sType =\n"
|
||||
" VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_INFO_EXT;\n"
|
||||
" fragmentDensityMap.pNext = renderInfo.pNext;\n"
|
||||
" fragmentDensityMap.imageView = densityMap;\n"
|
||||
" fragmentDensityMap.imageLayout = VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT;\n"
|
||||
" renderInfo.pNext = &fragmentDensityMap;\n"
|
||||
" }\n"
|
||||
" }\n\n",
|
||||
after=False)
|
||||
@@ -0,0 +1,21 @@
|
||||
// Included only by the pinned Dawn source build (apply.py): fragment density maps for Aurora's
|
||||
// immersive eye render passes. Implemented in aurora_fdm.inc.
|
||||
#pragma once
|
||||
#include "src/dawn/common/vulkan_platform.h"
|
||||
|
||||
namespace dawn::native {
|
||||
class TextureViewBase;
|
||||
}
|
||||
|
||||
namespace dawn::native::vulkan {
|
||||
class Device;
|
||||
struct VulkanDeviceInfo;
|
||||
|
||||
// Device::CreateDevice keeps VK_EXT_fragment_density_map only when Aurora asked for it and the
|
||||
// device can attach a map to a dynamic rendering pass over ordinary (non-subsampled) images.
|
||||
bool AuroraFdmWanted(const VulkanDeviceInfo& info, bool dynamicRendering);
|
||||
// The density map for a render pass whose first color attachment is `view`, or a null handle.
|
||||
::VkImageView AuroraFdmViewFor(Device* device, TextureViewBase* view);
|
||||
// Device::DestroyImpl: releases every map while the fenced deleter still runs.
|
||||
void AuroraFdmDestroy(Device* device);
|
||||
} // namespace dawn::native::vulkan
|
||||
@@ -0,0 +1,279 @@
|
||||
// Compiled inside VulkanBackend.cpp, using the pinned Dawn implementation (apply.py).
|
||||
//
|
||||
// Fragment density maps for Aurora's immersive eye render passes. Aurora uploads immutable RG8
|
||||
// maps, binds each to the texture view its eye passes render through, and
|
||||
// RecordBeginDynamicRenderPass chains the bound map into those passes. The maps are raw VkImages
|
||||
// that Dawn's usage tracking never sees: they stay in FRAGMENT_DENSITY_MAP_OPTIMAL from their
|
||||
// upload until the fenced deleter frees them.
|
||||
#define AURORA_DAWN_FDM_IMPLEMENTATION
|
||||
#include "aurora_dawn_fdm_abi.h"
|
||||
#include "aurora_fdm.h"
|
||||
#include "src/dawn/native/DynamicUploader.h"
|
||||
#include "src/dawn/native/ResourceMemoryAllocation.h"
|
||||
#include "src/dawn/native/vulkan/BufferVk.h"
|
||||
#include "src/dawn/native/vulkan/CommandRecordingContextVk.h"
|
||||
#include "src/dawn/native/vulkan/FencedDeleter.h"
|
||||
#include "src/dawn/native/vulkan/QueueVk.h"
|
||||
#include "src/dawn/native/vulkan/ResourceHeapVk.h"
|
||||
#include "src/dawn/native/vulkan/ResourceMemoryAllocatorVk.h"
|
||||
#include "src/dawn/native/vulkan/VulkanError.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
namespace dawn::native::vulkan {
|
||||
namespace {
|
||||
std::atomic<bool> auroraFdmRequested{false};
|
||||
|
||||
struct AuroraFdmMap {
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
ResourceMemoryAllocation memory;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
ExecutionSerial readySerial = kMaxExecutionSerial;
|
||||
};
|
||||
struct AuroraFdmBinding {
|
||||
Ref<TextureViewBase> view;
|
||||
uint64_t map = 0;
|
||||
};
|
||||
struct AuroraFdmState {
|
||||
uint64_t nextMap = 1;
|
||||
absl::flat_hash_map<uint64_t, AuroraFdmMap> maps;
|
||||
absl::flat_hash_map<TextureViewBase*, AuroraFdmBinding> bindings;
|
||||
};
|
||||
// Per device. Also taken while a render pass is recorded, under the device lock Queue::Submit holds.
|
||||
std::mutex auroraFdmMutex;
|
||||
absl::flat_hash_map<Device*, std::unique_ptr<AuroraFdmState>> auroraFdmStates;
|
||||
|
||||
Device* AuroraFdmDevice(void* handle) {
|
||||
return ToBackend(FromAPI(static_cast<WGPUDevice>(handle)));
|
||||
}
|
||||
|
||||
bool AuroraFdmEnabled(Device* device) {
|
||||
return device->GetDeviceInfo().HasExt(DeviceExt::FragmentDensityMap);
|
||||
}
|
||||
|
||||
AuroraFdmState* AuroraFdmStateFor(Device* device, bool create) {
|
||||
auto it = auroraFdmStates.find(device);
|
||||
if (it != auroraFdmStates.end()) return it->second.get();
|
||||
if (!create) return nullptr;
|
||||
return auroraFdmStates.emplace(device, std::make_unique<AuroraFdmState>()).first->second.get();
|
||||
}
|
||||
|
||||
bool AuroraFdmReady(Device* device, const AuroraFdmMap& map) {
|
||||
return device->GetQueue()->GetCompletedCommandSerial() >= map.readySerial;
|
||||
}
|
||||
|
||||
void AuroraFdmRelease(Device* device, AuroraFdmMap& map) {
|
||||
if (map.view != VK_NULL_HANDLE) device->GetFencedDeleter()->DeleteWhenUnused(map.view);
|
||||
if (map.image != VK_NULL_HANDLE) device->GetFencedDeleter()->DeleteWhenUnused(map.image);
|
||||
device->GetResourceMemoryAllocator()->Deallocate(&map.memory);
|
||||
map.view = VkImageView{};
|
||||
map.image = VkImage{};
|
||||
}
|
||||
|
||||
MaybeError AuroraFdmUpload(Device* device, uint32_t width, uint32_t height, const uint8_t* rg8, AuroraFdmMap& map) {
|
||||
VkImageCreateInfo imageInfo{};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = VK_FORMAT_R8G8_UNORM;
|
||||
imageInfo.extent = {width, height, 1};
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
DAWN_TRY(CheckVkSuccess(device->fn.CreateImage(device->GetVkDevice(), &imageInfo, nullptr, &*map.image),
|
||||
"CreateImage (fragment density map)"));
|
||||
|
||||
VkMemoryRequirements requirements;
|
||||
device->fn.GetImageMemoryRequirements(device->GetVkDevice(), map.image, &requirements);
|
||||
DAWN_TRY_ASSIGN(map.memory,
|
||||
device->GetResourceMemoryAllocator()->Allocate(requirements, MemoryKind::DeviceLocal));
|
||||
DAWN_TRY(CheckVkSuccess(device->fn.BindImageMemory(device->GetVkDevice(), map.image,
|
||||
ToBackend(map.memory.GetResourceHeap())->GetMemory(),
|
||||
map.memory.GetOffset()),
|
||||
"BindImageMemory (fragment density map)"));
|
||||
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = map.image;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = VK_FORMAT_R8G8_UNORM;
|
||||
viewInfo.components = {VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
||||
viewInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
DAWN_TRY(CheckVkSuccess(device->fn.CreateImageView(device->GetVkDevice(), &viewInfo, nullptr, &*map.view),
|
||||
"CreateImageView (fragment density map)"));
|
||||
|
||||
Queue* queue = ToBackend(device->GetQueue());
|
||||
const uint64_t size = uint64_t(width) * height * 2;
|
||||
DAWN_TRY(device->GetDynamicUploader()->WithUploadReservation(
|
||||
size, device->GetOptimalBufferToTextureCopyOffsetAlignment(),
|
||||
[&](UploadReservation reservation) -> MaybeError {
|
||||
std::memcpy(reservation.mappedPointer.get(), rg8, size);
|
||||
CommandRecordingContext* context = queue->GetPendingRecordingContext();
|
||||
|
||||
VkImageMemoryBarrier barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = map.image;
|
||||
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
device->fn.CmdPipelineBarrier(context->commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
|
||||
VkBufferImageCopy region{};
|
||||
region.bufferOffset = reservation.offsetInBuffer;
|
||||
region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
region.imageExtent = {width, height, 1};
|
||||
device->fn.CmdCopyBufferToImage(context->commandBuffer, ToBackend(reservation.buffer.Get())->GetHandle(),
|
||||
map.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
|
||||
// A map without VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT is read by the
|
||||
// host when a render pass using it is recorded, so the upload is also made visible to
|
||||
// host reads. The map is only used once this submission has completed.
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_FRAGMENT_DENSITY_MAP_READ_BIT_EXT | VK_ACCESS_HOST_READ_BIT;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT;
|
||||
device->fn.CmdPipelineBarrier(context->commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT_EXT |
|
||||
VK_PIPELINE_STAGE_HOST_BIT,
|
||||
0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
return {};
|
||||
}));
|
||||
map.width = width;
|
||||
map.height = height;
|
||||
map.readySerial = queue->GetPendingCommandSerial();
|
||||
DAWN_TRY(queue->SubmitPendingCommands());
|
||||
return {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool AuroraFdmWanted(const VulkanDeviceInfo& info, bool dynamicRendering) {
|
||||
return auroraFdmRequested.load() && dynamicRendering && info.HasExt(DeviceExt::FragmentDensityMap) &&
|
||||
info.fragmentDensityMapFeatures.fragmentDensityMap == VK_TRUE &&
|
||||
info.fragmentDensityMapFeatures.fragmentDensityMapNonSubsampledImages == VK_TRUE;
|
||||
}
|
||||
|
||||
::VkImageView AuroraFdmViewFor(Device* device, TextureViewBase* view) {
|
||||
if (view == nullptr || !AuroraFdmEnabled(device)) return VK_NULL_HANDLE;
|
||||
std::lock_guard lock(auroraFdmMutex);
|
||||
AuroraFdmState* state = AuroraFdmStateFor(device, false);
|
||||
if (state == nullptr) return VK_NULL_HANDLE;
|
||||
auto binding = state->bindings.find(view);
|
||||
if (binding == state->bindings.end()) return VK_NULL_HANDLE;
|
||||
auto map = state->maps.find(binding->second.map);
|
||||
if (map == state->maps.end() || !AuroraFdmReady(device, map->second)) return VK_NULL_HANDLE;
|
||||
return map->second.view;
|
||||
}
|
||||
|
||||
void AuroraFdmDestroy(Device* device) {
|
||||
std::unique_ptr<AuroraFdmState> state;
|
||||
{
|
||||
std::lock_guard lock(auroraFdmMutex);
|
||||
auto it = auroraFdmStates.find(device);
|
||||
if (it == auroraFdmStates.end()) return;
|
||||
state = std::move(it->second);
|
||||
auroraFdmStates.erase(it);
|
||||
}
|
||||
state->bindings.clear();
|
||||
for (auto& [id, map] : state->maps) AuroraFdmRelease(device, map);
|
||||
}
|
||||
|
||||
extern "C" DAWN_NATIVE_EXPORT uint32_t AuroraDawnFdmVersion(void) { return AURORA_DAWN_FDM_ABI; }
|
||||
|
||||
extern "C" DAWN_NATIVE_EXPORT void AuroraDawnFdmRequest(int enable) { auroraFdmRequested.store(enable != 0); }
|
||||
|
||||
extern "C" DAWN_NATIVE_EXPORT int AuroraDawnFdmQuery(void* handle, AuroraDawnFdmCaps* caps) {
|
||||
Device* device = AuroraFdmDevice(handle);
|
||||
auto guard = device->GetGuard();
|
||||
const VulkanDeviceInfo& info = device->GetDeviceInfo();
|
||||
*caps = {};
|
||||
caps->enabled = AuroraFdmEnabled(device) ? 1 : 0;
|
||||
if (info.HasExt(DeviceExt::FragmentDensityMap)) {
|
||||
const auto& properties = info.fragmentDensityMapProperties;
|
||||
caps->minTexelWidth = properties.minFragmentDensityTexelSize.width;
|
||||
caps->minTexelHeight = properties.minFragmentDensityTexelSize.height;
|
||||
caps->maxTexelWidth = properties.maxFragmentDensityTexelSize.width;
|
||||
caps->maxTexelHeight = properties.maxFragmentDensityTexelSize.height;
|
||||
}
|
||||
return static_cast<int>(caps->enabled);
|
||||
}
|
||||
|
||||
extern "C" DAWN_NATIVE_EXPORT uint64_t AuroraDawnFdmCreateMap(void* handle, uint32_t width, uint32_t height,
|
||||
const uint8_t* rg8) {
|
||||
Device* device = AuroraFdmDevice(handle);
|
||||
auto guard = device->GetGuard();
|
||||
if (!AuroraFdmEnabled(device) || width == 0 || height == 0 || rg8 == nullptr) return 0;
|
||||
AuroraFdmMap map;
|
||||
if (device->ConsumedError(AuroraFdmUpload(device, width, height, rg8, map))) {
|
||||
AuroraFdmRelease(device, map);
|
||||
return 0;
|
||||
}
|
||||
std::lock_guard lock(auroraFdmMutex);
|
||||
AuroraFdmState* state = AuroraFdmStateFor(device, true);
|
||||
const uint64_t id = state->nextMap++;
|
||||
state->maps.emplace(id, std::move(map));
|
||||
return id;
|
||||
}
|
||||
|
||||
extern "C" DAWN_NATIVE_EXPORT int AuroraDawnFdmMapReady(void* handle, uint64_t id) {
|
||||
Device* device = AuroraFdmDevice(handle);
|
||||
auto guard = device->GetGuard();
|
||||
std::lock_guard lock(auroraFdmMutex);
|
||||
AuroraFdmState* state = AuroraFdmStateFor(device, false);
|
||||
if (state == nullptr) return 0;
|
||||
auto map = state->maps.find(id);
|
||||
return map != state->maps.end() && AuroraFdmReady(device, map->second) ? 1 : 0;
|
||||
}
|
||||
|
||||
extern "C" DAWN_NATIVE_EXPORT void AuroraDawnFdmReleaseMap(void* handle, uint64_t id) {
|
||||
Device* device = AuroraFdmDevice(handle);
|
||||
auto guard = device->GetGuard();
|
||||
std::lock_guard lock(auroraFdmMutex);
|
||||
AuroraFdmState* state = AuroraFdmStateFor(device, false);
|
||||
if (state == nullptr) return;
|
||||
auto map = state->maps.find(id);
|
||||
if (map == state->maps.end()) return;
|
||||
absl::erase_if(state->bindings, [id](const auto& binding) { return binding.second.map == id; });
|
||||
AuroraFdmRelease(device, map->second);
|
||||
state->maps.erase(map);
|
||||
}
|
||||
|
||||
extern "C" DAWN_NATIVE_EXPORT int AuroraDawnFdmBind(void* handle, void* viewHandle, uint64_t id) {
|
||||
Device* device = AuroraFdmDevice(handle);
|
||||
auto guard = device->GetGuard();
|
||||
TextureViewBase* view = FromAPI(static_cast<WGPUTextureView>(viewHandle));
|
||||
if (view == nullptr) return 0;
|
||||
std::lock_guard lock(auroraFdmMutex);
|
||||
AuroraFdmState* state = AuroraFdmStateFor(device, id != 0);
|
||||
if (state == nullptr) return 1;
|
||||
if (id == 0) {
|
||||
state->bindings.erase(view);
|
||||
return 1;
|
||||
}
|
||||
auto map = state->maps.find(id);
|
||||
if (map == state->maps.end()) return 0;
|
||||
// VkRenderingInfo requires the map to reach the render area at the largest texel size.
|
||||
const Extent3D size = view->GetSingleSubresourceVirtualSize();
|
||||
const auto& properties = device->GetDeviceInfo().fragmentDensityMapProperties;
|
||||
const uint64_t maxTexelWidth = std::max<uint32_t>(properties.maxFragmentDensityTexelSize.width, 1);
|
||||
const uint64_t maxTexelHeight = std::max<uint32_t>(properties.maxFragmentDensityTexelSize.height, 1);
|
||||
if (map->second.width * maxTexelWidth < size.width || map->second.height * maxTexelHeight < size.height) {
|
||||
return 0;
|
||||
}
|
||||
state->bindings[view] = AuroraFdmBinding{Ref<TextureViewBase>(view), id};
|
||||
return 1;
|
||||
}
|
||||
} // namespace dawn::native::vulkan
|
||||
Reference in new issue
Block a user