diff --git a/CMakeLists.txt b/CMakeLists.txt index fcd76361a..286464b97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,10 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") project("MarathonRecomp-ALL") +if(APPLE) + enable_language(OBJC OBJCXX) +endif() + if (CMAKE_OSX_ARCHITECTURES) set(MARATHON_RECOMP_ARCHITECTURE ${CMAKE_OSX_ARCHITECTURES}) elseif(CMAKE_SYSTEM_PROCESSOR) diff --git a/MarathonRecomp/CMakeLists.txt b/MarathonRecomp/CMakeLists.txt index 4ea63dc56..9b429a1cf 100644 --- a/MarathonRecomp/CMakeLists.txt +++ b/MarathonRecomp/CMakeLists.txt @@ -1,7 +1,7 @@ project("MarathonRecomp") if (WIN32) - option(MARATHON_RECOMP_D3D12 "Add D3D12 support for rendering" OFF) + option(MARATHON_RECOMP_D3D12 "Add D3D12 support for rendering" ON) endif() if (CMAKE_SYSTEM_NAME MATCHES "Linux") @@ -125,6 +125,12 @@ if (MARATHON_RECOMP_D3D12) ) endif() +if (APPLE) + list(APPEND MARATHON_RECOMP_GPU_CXX_SOURCES + "gpu/rhi/plume_apple.mm" + ) +endif() + set(MARATHON_RECOMP_APU_CXX_SOURCES "apu/audio.cpp" "apu/xma_decoder.cpp" @@ -381,31 +387,28 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") target_compile_definitions(MarathonRecomp PRIVATE SDL_VULKAN_ENABLED) endif() -#find_package(directx-dxc REQUIRED) find_package(CURL REQUIRED) -#if (MARATHON_RECOMP_D3D12) -# file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) -# add_custom_command(TARGET MarathonRecomp POST_BUILD -# COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_CURRENT_BINARY_DIR}/D3D12 -# COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_CURRENT_BINARY_DIR}/D3D12 -# COMMAND_EXPAND_LISTS -# ) -# -# find_file(DIRECTX_DXIL_LIBRARY "dxil.dll") -# file(COPY ${DIRECTX_DXIL_LIBRARY} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -# -# target_link_libraries(MarathonRecomp PRIVATE -# Microsoft::DirectX-Headers -# Microsoft::DirectX-Guids -# Microsoft::DirectX12-Agility -# Microsoft::DirectXShaderCompiler -# Microsoft::DXIL -# dxgi -# ) -#endif() - -#file(CHMOD ${DIRECTX_DXC_TOOL} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) + +if (MARATHON_RECOMP_D3D12) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) + add_custom_command(TARGET MarathonRecomp POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $/D3D12 + COMMAND ${CMAKE_COMMAND} -E copy $ $/D3D12 + COMMAND ${CMAKE_COMMAND} -E copy $ $ + COMMAND ${CMAKE_COMMAND} -E copy $ $ + COMMAND_EXPAND_LISTS + ) + + target_link_libraries(MarathonRecomp PRIVATE + Microsoft::DirectX-Headers + Microsoft::DirectX-Guids + Microsoft::DirectX12-Agility + Microsoft::DirectXShaderCompiler + Microsoft::DXIL + dxgi + ) +endif() if (WIN32) target_link_libraries(MarathonRecomp PRIVATE diff --git a/MarathonRecomp/gpu/rhi/plume_apple.h b/MarathonRecomp/gpu/rhi/plume_apple.h new file mode 100644 index 000000000..731d3c443 --- /dev/null +++ b/MarathonRecomp/gpu/rhi/plume_apple.h @@ -0,0 +1,43 @@ +// +// plume +// +// Copyright (c) 2024 renderbag and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file for details. +// + +#pragma once + +#include +#include +#include "plume_render_interface_types.h" + +namespace plume { + RenderDeviceVendor getRenderDeviceVendor(uint64_t registryID); + + struct CocoaWindowAttributes { + int x, y; + int width, height; + }; + + class CocoaWindow { + void* windowHandle; + CocoaWindowAttributes cachedAttributes; + std::atomic cachedRefreshRate; + mutable std::mutex attributesMutex; + + void updateWindowAttributesInternal(bool forceSync = false); + void updateRefreshRateInternal(bool forceSync = false); + public: + CocoaWindow(void* window); + ~CocoaWindow(); + + // Get cached window attributes, may trigger async update + void getWindowAttributes(CocoaWindowAttributes* attributes) const; + + // Get cached refresh rate, may trigger async update + int getRefreshRate() const; + + // Toggle fullscreen + void toggleFullscreen(); + }; +} diff --git a/MarathonRecomp/gpu/rhi/plume_apple.mm b/MarathonRecomp/gpu/rhi/plume_apple.mm new file mode 100644 index 000000000..64e4dc9ff --- /dev/null +++ b/MarathonRecomp/gpu/rhi/plume_apple.mm @@ -0,0 +1,170 @@ +// +// plume +// +// Copyright (c) 2024 renderbag and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file for details. +// + +#include "plume_apple.h" + +#import +#import +#import + +static uint32_t plumeGetEntryProperty(io_registry_entry_t entry, CFStringRef propertyName) { + uint32_t value = 0; + CFTypeRef cfProp = IORegistryEntrySearchCFProperty(entry, kIOServicePlane, propertyName, kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents); + + if (cfProp) { + if (CFGetTypeID(cfProp) == CFDataGetTypeID()) { + const uint32_t* pValue = reinterpret_cast(CFDataGetBytePtr((CFDataRef)cfProp)); + if (pValue) { + value = *pValue; + } + } + CFRelease(cfProp); + } + + return value; +} + +namespace plume { + RenderDeviceVendor getRenderDeviceVendor(uint64_t registryID) { + io_service_t entry = IOServiceGetMatchingService(MACH_PORT_NULL, IORegistryEntryIDMatching(registryID)); + + if (entry) { + io_registry_entry_t parent; + if (IORegistryEntryGetParentEntry(entry, kIOServicePlane, &parent) == kIOReturnSuccess) { + uint32_t vendorId = plumeGetEntryProperty(parent, CFSTR("vendor-id")); + IOObjectRelease(parent); // Release the parent + IOObjectRelease(entry); // Release the entry + return RenderDeviceVendor(vendorId); + } + IOObjectRelease(entry); // Release the entry if we couldn't get parent + } + + return RenderDeviceVendor::UNKNOWN; + } + + // MARK: - CocoaWindow + + CocoaWindow::CocoaWindow(void* window) + : windowHandle(window), cachedRefreshRate(0) { + cachedAttributes = {0, 0, 0, 0}; + + if ([NSThread isMainThread]) { + NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; + NSRect contentFrame = [[nsWindow contentView] frame]; + CGFloat scaleFactor = [nsWindow backingScaleFactor]; + + cachedAttributes.x = (int)round(contentFrame.origin.x); + cachedAttributes.y = (int)round(contentFrame.origin.y); + cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor); + cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor); + + NSScreen *screen = [nsWindow screen]; + if (@available(macOS 12.0, *)) { + cachedRefreshRate.store((int)[screen maximumFramesPerSecond]); + } + } else { + updateWindowAttributesInternal(true); + updateRefreshRateInternal(true); + } + } + + CocoaWindow::~CocoaWindow() {} + + void CocoaWindow::updateWindowAttributesInternal(bool forceSync) { + auto updateBlock = ^{ + NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; + NSRect contentFrame = [[nsWindow contentView] frame]; + CGFloat scaleFactor = [nsWindow backingScaleFactor]; + + std::lock_guard lock(attributesMutex); + cachedAttributes.x = (int)round(contentFrame.origin.x); + cachedAttributes.y = (int)round(contentFrame.origin.y); + cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor); + cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor); + }; + + if (forceSync) { + dispatch_sync(dispatch_get_main_queue(), updateBlock); + } else { + dispatch_async(dispatch_get_main_queue(), updateBlock); + } + } + + void CocoaWindow::updateRefreshRateInternal(bool forceSync) { + auto updateBlock = ^{ + NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; + NSScreen *screen = [nsWindow screen]; + if (@available(macOS 12.0, *)) { + cachedRefreshRate.store((int)[screen maximumFramesPerSecond]); + } + }; + + if (forceSync) { + dispatch_sync(dispatch_get_main_queue(), updateBlock); + } else { + dispatch_async(dispatch_get_main_queue(), updateBlock); + } + } + + void CocoaWindow::getWindowAttributes(CocoaWindowAttributes* attributes) const { + if ([NSThread isMainThread]) { + NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; + NSRect contentFrame = [[nsWindow contentView] frame]; + CGFloat scaleFactor = [nsWindow backingScaleFactor]; + + { + std::lock_guard lock(attributesMutex); + const_cast(this)->cachedAttributes.x = (int)round(contentFrame.origin.x); + const_cast(this)->cachedAttributes.y = (int)round(contentFrame.origin.y); + const_cast(this)->cachedAttributes.width = (int)round(contentFrame.size.width * scaleFactor); + const_cast(this)->cachedAttributes.height = (int)round(contentFrame.size.height * scaleFactor); + + *attributes = cachedAttributes; + } + } else { + { + std::lock_guard lock(attributesMutex); + *attributes = cachedAttributes; + } + + const_cast(this)->updateWindowAttributesInternal(false); + } + } + + int CocoaWindow::getRefreshRate() const { + if ([NSThread isMainThread]) { + NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; + NSScreen *screen = [nsWindow screen]; + + if (@available(macOS 12.0, *)) { + int freshRate = (int)[screen maximumFramesPerSecond]; + const_cast(this)->cachedRefreshRate.store(freshRate); + return freshRate; + } + + return cachedRefreshRate.load(); + } else { + int rate = cachedRefreshRate.load(); + + const_cast(this)->updateRefreshRateInternal(false); + + return rate; + } + } + + void CocoaWindow::toggleFullscreen() { + if ([NSThread isMainThread]) { + NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; + [nsWindow toggleFullScreen:NULL]; + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + NSWindow *nsWindow = (__bridge NSWindow *)windowHandle; + [nsWindow toggleFullScreen:NULL]; + }); + } + } +} diff --git a/MarathonRecomp/gpu/rhi/plume_d3d12.cpp b/MarathonRecomp/gpu/rhi/plume_d3d12.cpp index cd4366103..ae9e50447 100644 --- a/MarathonRecomp/gpu/rhi/plume_d3d12.cpp +++ b/MarathonRecomp/gpu/rhi/plume_d3d12.cpp @@ -483,9 +483,11 @@ namespace plume { case RenderPrimitiveTopology::TRIANGLE_LIST: return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; case RenderPrimitiveTopology::TRIANGLE_STRIP: - return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; +# ifdef D3D12_AGILITY_SDK_ENABLED case RenderPrimitiveTopology::TRIANGLE_FAN: return D3D_PRIMITIVE_TOPOLOGY_TRIANGLEFAN; +# endif default: assert(false && "Unknown primitive topology."); return D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; @@ -624,9 +626,10 @@ namespace plume { switch (location.type) { case RenderTextureCopyType::SUBRESOURCE: { const D3D12Texture *interfaceTexture = static_cast(location.texture); + uint32_t mipLevels = interfaceTexture->desc.mipLevels; loc.pResource = (interfaceTexture != nullptr) ? interfaceTexture->d3d : nullptr; loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - loc.SubresourceIndex = location.subresource.index; + loc.SubresourceIndex = location.subresource.mipLevel + location.subresource.arrayIndex * mipLevels; break; } case RenderTextureCopyType::PLACED_FOOTPRINT: { @@ -1052,26 +1055,60 @@ namespace plume { const bool isMSAA = (interfaceTextureView->texture->desc.multisampling.sampleCount > RenderSampleCount::COUNT_1); switch (interfaceTextureView->dimension) { case RenderTextureViewDimension::TEXTURE_1D: - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; - srvDesc.Texture1D.MipLevels = interfaceTextureView->mipLevels; + if (interfaceTextureView->arraySize > 1) { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY; + srvDesc.Texture1DArray.MipLevels = interfaceTextureView->mipLevels; + srvDesc.Texture1DArray.MostDetailedMip = interfaceTextureView->mipSlice; + srvDesc.Texture1DArray.FirstArraySlice = interfaceTextureView->arrayIndex; + srvDesc.Texture1DArray.ArraySize = interfaceTextureView->arraySize; + } else { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D; + srvDesc.Texture1D.MipLevels = interfaceTextureView->mipLevels; + srvDesc.Texture1D.MostDetailedMip = interfaceTextureView->mipSlice; + } break; case RenderTextureViewDimension::TEXTURE_2D: if (isMSAA) { - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS; + if (interfaceTextureView->arraySize > 1) { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY; + srvDesc.Texture2DMSArray.FirstArraySlice = interfaceTextureView->arrayIndex; + srvDesc.Texture2DMSArray.ArraySize = interfaceTextureView->arraySize; + } else { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS; + } } else { - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MipLevels = interfaceTextureView->mipLevels; + if (interfaceTextureView->arraySize > 1) { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + srvDesc.Texture2DArray.MipLevels = interfaceTextureView->mipLevels; + srvDesc.Texture2DArray.MostDetailedMip = interfaceTextureView->mipSlice; + srvDesc.Texture2DArray.FirstArraySlice = interfaceTextureView->arrayIndex; + srvDesc.Texture2DArray.ArraySize = interfaceTextureView->arraySize; + } else { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = interfaceTextureView->mipLevels; + srvDesc.Texture2D.MostDetailedMip = interfaceTextureView->mipSlice; + } } break; case RenderTextureViewDimension::TEXTURE_3D: srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE3D; srvDesc.Texture3D.MipLevels = interfaceTextureView->mipLevels; + srvDesc.Texture3D.MostDetailedMip = interfaceTextureView->mipSlice; break; case RenderTextureViewDimension::TEXTURE_CUBE: - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; - srvDesc.TextureCube.MipLevels = interfaceTextureView->mipLevels; + if (interfaceTextureView->arraySize > 6) { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; + srvDesc.TextureCubeArray.MipLevels = interfaceTextureView->mipLevels; + srvDesc.TextureCubeArray.MostDetailedMip = interfaceTextureView->mipSlice; + srvDesc.TextureCubeArray.First2DArrayFace = interfaceTextureView->arrayIndex; + srvDesc.TextureCubeArray.NumCubes = interfaceTextureView->arraySize / 6; + } else { + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE; + srvDesc.TextureCube.MipLevels = interfaceTextureView->mipLevels; + srvDesc.TextureCube.MostDetailedMip = interfaceTextureView->mipSlice; + } break; default: assert(false && "Unknown texture dimension."); @@ -1101,12 +1138,26 @@ namespace plume { switch (interfaceTextureView->dimension) { case RenderTextureViewDimension::TEXTURE_1D: - uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D; - uavDesc.Texture1D.MipSlice = interfaceTextureView->mipSlice; + if (interfaceTextureView->arraySize > 1) { + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY; + uavDesc.Texture1DArray.MipSlice = interfaceTextureView->mipSlice; + uavDesc.Texture1DArray.FirstArraySlice = interfaceTextureView->arrayIndex; + uavDesc.Texture1DArray.ArraySize = interfaceTextureView->arraySize; + } else { + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D; + uavDesc.Texture1D.MipSlice = interfaceTextureView->mipSlice; + } break; case RenderTextureViewDimension::TEXTURE_2D: - uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; - uavDesc.Texture2D.MipSlice = interfaceTextureView->mipSlice; + if (interfaceTextureView->arraySize > 1) { + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + uavDesc.Texture2DArray.MipSlice = interfaceTextureView->mipSlice; + uavDesc.Texture2DArray.FirstArraySlice = interfaceTextureView->arrayIndex; + uavDesc.Texture2DArray.ArraySize = interfaceTextureView->arraySize; + } else { + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + uavDesc.Texture2D.MipSlice = interfaceTextureView->mipSlice; + } break; case RenderTextureViewDimension::TEXTURE_3D: uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D; @@ -1267,8 +1318,6 @@ namespace plume { D3D12SwapChain::~D3D12SwapChain() { for (uint32_t i = 0; i < textureCount; i++) { - textures[i].releaseTargetHeap(); - if (textures[i].d3d != nullptr) { textures[i].d3d->Release(); textures[i].d3d = nullptr; @@ -1302,7 +1351,6 @@ namespace plume { } for (uint32_t i = 0; i < textureCount; i++) { - textures[i].releaseTargetHeap(); textures[i].d3d->Release(); textures[i].d3d = nullptr; } @@ -1356,7 +1404,6 @@ namespace plume { textures[i].desc.height = height; textures[i].resourceStates = D3D12_RESOURCE_STATE_PRESENT; textures[i].layout = RenderTextureLayout::PRESENT; - textures[i].createRenderTargetHeap(); } } @@ -1395,10 +1442,10 @@ namespace plume { if (desc.colorAttachmentsCount > 0) { for (uint32_t i = 0; i < desc.colorAttachmentsCount; i++) { - const D3D12Texture *interfaceTexture = static_cast(desc.colorAttachments[i]); + const D3D12TextureView *interfaceTextureView = desc.colorAttachmentViews ? static_cast(desc.colorAttachmentViews[i]) : nullptr; + const D3D12Texture *interfaceTexture = interfaceTextureView ? interfaceTextureView->texture : static_cast(desc.colorAttachments[i]); assert((interfaceTexture->desc.flags & RenderTextureFlag::RENDER_TARGET) && "Color attachment must be a render target."); - colorTargets.emplace_back(interfaceTexture); - colorHandles.emplace_back(device->colorTargetHeapAllocator->getCPUHandleAt(interfaceTexture->targetAllocatorOffset)); + createRenderTargetHeap(interfaceTexture, interfaceTextureView); if (i == 0) { width = interfaceTexture->desc.width; @@ -1407,18 +1454,11 @@ namespace plume { } } - if (desc.depthAttachment != nullptr) { - const D3D12Texture *interfaceTexture = static_cast(desc.depthAttachment); + if (desc.depthAttachment != nullptr || desc.depthAttachmentView != nullptr) { + const D3D12TextureView *interfaceTextureView = static_cast(desc.depthAttachmentView); + const D3D12Texture *interfaceTexture = interfaceTextureView ? interfaceTextureView->texture : static_cast(desc.depthAttachment); assert((interfaceTexture->desc.flags & RenderTextureFlag::DEPTH_TARGET) && "Depth attachment must be a depth target."); - depthTarget = interfaceTexture; - - // The read-only handle is on the second slot on the DSV heap. - if (desc.depthAttachmentReadOnly) { - depthHandle = device->depthTargetHeapAllocator->getCPUHandleAt(interfaceTexture->targetAllocatorOffset + 1); - } - else { - depthHandle = device->depthTargetHeapAllocator->getCPUHandleAt(interfaceTexture->targetAllocatorOffset); - } + createDepthStencilHeap(interfaceTexture, interfaceTextureView, desc.depthAttachmentReadOnly); if (desc.colorAttachmentsCount == 0) { width = interfaceTexture->desc.width; @@ -1427,7 +1467,9 @@ namespace plume { } } - D3D12Framebuffer::~D3D12Framebuffer() { } + D3D12Framebuffer::~D3D12Framebuffer() { + releaseTargetHeap(); + } uint32_t D3D12Framebuffer::getWidth() const { return width; @@ -1437,6 +1479,237 @@ namespace plume { return height; } + void D3D12Framebuffer::createRenderTargetHeap(const D3D12Texture* texture, const D3D12TextureView* textureView) { + const uint32_t targetAllocatorOffset = device->colorTargetHeapAllocator->allocate(1); + if (targetAllocatorOffset == D3D12DescriptorHeapAllocator::INVALID_OFFSET) { + fprintf(stderr, "Allocator was unable to find free space for the set."); + return; + } + + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = toDXGI(textureView ? textureView->desc.format : texture->desc.format); + + const bool isMSAA = (texture->desc.multisampling.sampleCount > RenderSampleCount::COUNT_1); + if (textureView) { + switch (textureView->desc.dimension) { + case RenderTextureViewDimension::TEXTURE_1D: + if (texture->desc.arraySize > 1) { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY; + rtvDesc.Texture1D.MipSlice = textureView->desc.mipSlice; + rtvDesc.Texture1DArray.FirstArraySlice = textureView->desc.arrayIndex; + rtvDesc.Texture1DArray.ArraySize = textureView->desc.arraySize; + } else { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D; + rtvDesc.Texture1D.MipSlice = textureView->desc.mipSlice; + } + break; + case RenderTextureViewDimension::TEXTURE_2D: + if (texture->desc.arraySize > 1) { + if (isMSAA) { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; + rtvDesc.Texture2DMSArray.FirstArraySlice = textureView->desc.arrayIndex; + rtvDesc.Texture2DMSArray.ArraySize = textureView->desc.arraySize; + } + else { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.MipSlice = textureView->desc.mipSlice; + rtvDesc.Texture2DArray.FirstArraySlice = textureView->desc.arrayIndex; + rtvDesc.Texture2DArray.ArraySize = textureView->desc.arraySize; + } + } else { + if (isMSAA) { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; + } + else { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = textureView->desc.mipSlice; + } + } + + break; + case RenderTextureViewDimension::TEXTURE_3D: + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; + rtvDesc.Texture3D.MipSlice = textureView->desc.mipSlice; + rtvDesc.Texture3D.FirstWSlice = 0; + rtvDesc.Texture3D.WSize = 1; + break; + default: + assert(false && "Unsupported texture dimension for render target."); + break; + } + } else { + switch (texture->desc.dimension) { + case RenderTextureDimension::TEXTURE_1D: + if (texture->desc.arraySize > 1) { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1DARRAY; + rtvDesc.Texture1DArray.MipSlice = 0; + rtvDesc.Texture1DArray.FirstArraySlice = 0; + rtvDesc.Texture1DArray.ArraySize = texture->desc.arraySize; + } else { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D; + rtvDesc.Texture1D.MipSlice = 0; + } + break; + case RenderTextureDimension::TEXTURE_2D: + if (texture->desc.arraySize > 1) { + if (isMSAA) { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY; + rtvDesc.Texture2DMSArray.FirstArraySlice = 0; + rtvDesc.Texture2DMSArray.ArraySize = texture->desc.arraySize; + } + else { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + rtvDesc.Texture2DArray.MipSlice = 0; + rtvDesc.Texture2DArray.FirstArraySlice = 0; + rtvDesc.Texture2DArray.ArraySize = texture->desc.arraySize; + } + } else { + if (isMSAA) { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; + } + else { + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = 0; + } + } + + break; + case RenderTextureDimension::TEXTURE_3D: + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; + rtvDesc.Texture3D.MipSlice = 0; + rtvDesc.Texture3D.FirstWSlice = 0; + rtvDesc.Texture3D.WSize = 1; + break; + default: + assert(false && "Unsupported texture dimension for render target."); + break; + } + } + + const D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = device->colorTargetHeapAllocator->getCPUHandleAt(targetAllocatorOffset); + device->d3d->CreateRenderTargetView(texture->d3d, &rtvDesc, cpuHandle); + + colorTargets.emplace_back(texture); + colorHandles.emplace_back(cpuHandle); + colorTargetAllocatorOffsets.emplace_back(targetAllocatorOffset); + } + + void D3D12Framebuffer::createDepthStencilHeap(const D3D12Texture* texture, const D3D12TextureView* textureView, const bool readOnly) { + const uint32_t targetAllocatorOffset = device->depthTargetHeapAllocator->allocate(2); + if (targetAllocatorOffset == D3D12DescriptorHeapAllocator::INVALID_OFFSET) { + fprintf(stderr, "Allocator was unable to find free space for the set."); + return; + } + + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; + dsvDesc.Format = toDXGI(textureView ? textureView->desc.format : texture->desc.format); + + const bool isMSAA = (texture->desc.multisampling.sampleCount > RenderSampleCount::COUNT_1); + if (textureView) { + switch (textureView->desc.dimension) { + case RenderTextureViewDimension::TEXTURE_1D: + if (texture->desc.arraySize > 1) { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1DARRAY; + dsvDesc.Texture1D.MipSlice = textureView->desc.mipSlice; + dsvDesc.Texture1DArray.FirstArraySlice = textureView->desc.arrayIndex; + dsvDesc.Texture1DArray.ArraySize = textureView->desc.arraySize; + } else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1D; + dsvDesc.Texture1D.MipSlice = textureView->desc.mipSlice; + } + break; + case RenderTextureViewDimension::TEXTURE_2D: + if (texture->desc.arraySize > 1) { + if (isMSAA) { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY; + dsvDesc.Texture2DMSArray.FirstArraySlice = textureView->desc.arrayIndex; + dsvDesc.Texture2DMSArray.ArraySize = textureView->desc.arraySize; + } + else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; + dsvDesc.Texture2DArray.MipSlice = textureView->desc.mipSlice; + dsvDesc.Texture2DArray.FirstArraySlice = textureView->desc.arrayIndex; + dsvDesc.Texture2DArray.ArraySize = textureView->desc.arraySize; + } + } else { + if (isMSAA) { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; + } + else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + dsvDesc.Texture2D.MipSlice = textureView->desc.mipSlice; + } + } + + break; + default: + assert(false && "Unsupported texture dimension for render target."); + break; + } + } else { + switch (texture->desc.dimension) { + case RenderTextureDimension::TEXTURE_1D: + if (texture->desc.arraySize > 1) { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1DARRAY; + dsvDesc.Texture1DArray.MipSlice = 0; + dsvDesc.Texture1DArray.FirstArraySlice = 0; + dsvDesc.Texture1DArray.ArraySize = texture->desc.arraySize; + } else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1D; + dsvDesc.Texture1D.MipSlice = 0; + } + break; + case RenderTextureDimension::TEXTURE_2D: + if (texture->desc.arraySize > 1) { + if (isMSAA) { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY; + dsvDesc.Texture2DMSArray.FirstArraySlice = 0; + dsvDesc.Texture2DMSArray.ArraySize = texture->desc.arraySize; + } + else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; + dsvDesc.Texture2DArray.MipSlice = 0; + dsvDesc.Texture2DArray.FirstArraySlice = 0; + dsvDesc.Texture2DArray.ArraySize = texture->desc.arraySize; + } + } else { + if (isMSAA) { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; + } + else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + dsvDesc.Texture2D.MipSlice = 0; + } + } + + break; + default: + assert(false && "Unsupported texture dimension for render target."); + break; + } + } + + if (readOnly) { + dsvDesc.Flags = D3D12_DSV_FLAG_READ_ONLY_DEPTH; + } + + const D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = device->depthTargetHeapAllocator->getCPUHandleAt(targetAllocatorOffset); + device->d3d->CreateDepthStencilView(texture->d3d, &dsvDesc, cpuHandle); + + depthTarget = texture; + depthHandle = cpuHandle; + depthTargetAllocatorOffset = targetAllocatorOffset; + } + + void D3D12Framebuffer::releaseTargetHeap() { + for (const uint32_t targetAllocatorOffset : colorTargetAllocatorOffsets) { + device->colorTargetHeapAllocator->free(targetAllocatorOffset, 1); + } + if (depthTargetAllocatorOffset) { + device->depthTargetHeapAllocator->free(depthTargetAllocatorOffset, 1); + } + } + // D3D12QueryPool D3D12QueryPool::D3D12QueryPool(D3D12Device *device, uint32_t queryCount) { @@ -1485,14 +1758,13 @@ namespace plume { // D3D12CommandList - D3D12CommandList::D3D12CommandList(D3D12Device *device, RenderCommandListType type) { - assert(device != nullptr); + D3D12CommandList::D3D12CommandList(D3D12CommandQueue *queue) { + assert(queue != nullptr); - this->device = device; - this->type = type; + this->queue = queue; D3D12_COMMAND_LIST_TYPE commandListType; - switch (type) { + switch (queue->type) { case RenderCommandListType::DIRECT: commandListType = D3D12_COMMAND_LIST_TYPE_DIRECT; break; @@ -1507,13 +1779,13 @@ namespace plume { return; } - HRESULT res = device->d3d->CreateCommandAllocator(commandListType, IID_PPV_ARGS(&commandAllocator)); + HRESULT res = queue->device->d3d->CreateCommandAllocator(commandListType, IID_PPV_ARGS(&commandAllocator)); if (FAILED(res)) { fprintf(stderr, "CreateCommandAllocator failed with error code 0x%lX.\n", res); return; } - res = device->d3d->CreateCommandList(0, commandListType, commandAllocator, nullptr, IID_PPV_ARGS(&d3d)); + res = queue->device->d3d->CreateCommandList(0, commandListType, commandAllocator, nullptr, IID_PPV_ARGS(&d3d)); if (FAILED(res)) { fprintf(stderr, "CreateCommandList failed with error code 0x%lX.\n", res); return; @@ -1878,8 +2150,12 @@ namespace plume { } void D3D12CommandList::setDepthBias(float depthBias, float depthBiasClamp, float slopeScaledDepthBias) { - assert(device->capabilities.dynamicDepthBias && "Dynamic depth bias is unsupported on this device."); +# ifdef D3D12_AGILITY_SDK_ENABLED + assert(queue->device->capabilities.dynamicDepthBias && "Dynamic depth bias is unsupported on this device."); d3d->RSSetDepthBias(depthBias, depthBiasClamp, slopeScaledDepthBias); +# else + assert(false && "Dynamic depth bias is unsupported without the Agility SDK."); +# endif } void D3D12CommandList::clearColor(uint32_t attachmentIndex, RenderColor colorValue, const RenderRect *clearRects, uint32_t clearRectsCount) { @@ -2065,7 +2341,7 @@ namespace plume { void D3D12CommandList::checkDescriptorHeaps() { if (!descriptorHeapsSet) { - ID3D12DescriptorHeap *descriptorHeaps[] = { device->viewHeapAllocator->heap, device->samplerHeapAllocator->heap }; + ID3D12DescriptorHeap *descriptorHeaps[] = { queue->device->viewHeapAllocator->heap, queue->device->samplerHeapAllocator->heap }; d3d->SetDescriptorHeaps(std::size(descriptorHeaps), descriptorHeaps); descriptorHeapsSet = true; } @@ -2134,8 +2410,8 @@ namespace plume { checkDescriptorHeaps(); D3D12DescriptorSet *interfaceDescriptorSet = static_cast(descriptorSet); - setRootDescriptorTable(device->viewHeapAllocator.get(), interfaceDescriptorSet->viewAllocation, activePipelineLayout->setViewRootIndices[setIndex], setCompute); - setRootDescriptorTable(device->samplerHeapAllocator.get(), interfaceDescriptorSet->samplerAllocation, activePipelineLayout->setSamplerRootIndices[setIndex], setCompute); + setRootDescriptorTable(queue->device->viewHeapAllocator.get(), interfaceDescriptorSet->viewAllocation, activePipelineLayout->setViewRootIndices[setIndex], setCompute); + setRootDescriptorTable(queue->device->samplerHeapAllocator.get(), interfaceDescriptorSet->samplerAllocation, activePipelineLayout->setSamplerRootIndices[setIndex], setCompute); } void D3D12CommandList::setRootDescriptorTable(D3D12DescriptorHeapAllocator *heapAllocator, D3D12DescriptorSet::HeapAllocation &heapAllocation, uint32_t rootIndex, bool setCompute) { @@ -2280,11 +2556,18 @@ namespace plume { } } + std::unique_ptr D3D12CommandQueue::createCommandList() { + return std::make_unique(this); + } + std::unique_ptr D3D12CommandQueue::createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format, uint32_t maxFrameLatency) { return std::make_unique(this, renderWindow, bufferCount, format, maxFrameLatency); } void D3D12CommandQueue::executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) { + assert(commandLists != nullptr); + assert(commandListCount > 0); + for (uint32_t i = 0; i < waitSemaphoreCount; i++) { D3D12CommandSemaphore *interfaceSemaphore = static_cast(waitSemaphores[i]); d3d->Wait(interfaceSemaphore->d3d, interfaceSemaphore->semaphoreValue); @@ -2428,12 +2711,17 @@ namespace plume { D3D12TextureView::D3D12TextureView(D3D12Texture *texture, const RenderTextureViewDesc &desc) { assert(texture != nullptr); + assert(desc.mipSlice < texture->desc.mipLevels); + assert(desc.arrayIndex < texture->desc.arraySize); this->texture = texture; + this->desc = desc; this->format = toDXGI(desc.format); this->dimension = desc.dimension; - this->mipLevels = desc.mipLevels; + this->mipLevels = std::min(desc.mipLevels, texture->desc.mipLevels - desc.mipSlice); this->mipSlice = desc.mipSlice; + this->arraySize = std::min(desc.arraySize, texture->desc.arraySize - desc.arrayIndex); + this->arrayIndex = desc.arrayIndex; this->shader4ComponentMapping = toD3D12(desc.componentMapping); // D3D12 and Vulkan disagree on whether D32 is usable as a texture view format. We just make D3D12 use R32 instead. @@ -2484,18 +2772,9 @@ namespace plume { fprintf(stderr, "CreateResource failed with error code 0x%lX.\n", res); return; } - - if (renderTarget) { - createRenderTargetHeap(); - } - else if (depthTarget) { - createDepthStencilHeap(); - } } D3D12Texture::~D3D12Texture() { - releaseTargetHeap(); - if (allocation != nullptr) { d3d->Release(); allocation->Release(); @@ -2510,106 +2789,6 @@ namespace plume { setObjectName(d3d, name); } - void D3D12Texture::createRenderTargetHeap() { - targetAllocatorOffset = device->colorTargetHeapAllocator->allocate(1); - if (targetAllocatorOffset == D3D12DescriptorHeapAllocator::INVALID_OFFSET) { - fprintf(stderr, "Allocator was unable to find free space for the set."); - return; - } - - targetEntryCount = 1; - targetHeapDepth = false; - - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; - rtvDesc.Format = toDXGI(desc.format); - - const bool isMSAA = (desc.multisampling.sampleCount > RenderSampleCount::COUNT_1); - switch (desc.dimension) { - case RenderTextureDimension::TEXTURE_1D: - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE1D; - rtvDesc.Texture1D.MipSlice = 0; - break; - case RenderTextureDimension::TEXTURE_2D: - if (isMSAA) { - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; - } - else { - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; - rtvDesc.Texture2D.MipSlice = 0; - rtvDesc.Texture2D.PlaneSlice = 0; - } - - break; - case RenderTextureDimension::TEXTURE_3D: - rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D; - rtvDesc.Texture3D.MipSlice = 0; - rtvDesc.Texture3D.FirstWSlice = 0; - rtvDesc.Texture3D.WSize = 1; - break; - default: - assert(false && "Unsupported texture dimension for render target."); - break; - } - - const D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = device->colorTargetHeapAllocator->getCPUHandleAt(targetAllocatorOffset); - device->d3d->CreateRenderTargetView(d3d, &rtvDesc, cpuHandle); - } - - void D3D12Texture::createDepthStencilHeap() { - targetAllocatorOffset = device->depthTargetHeapAllocator->allocate(2); - if (targetAllocatorOffset == D3D12DescriptorHeapAllocator::INVALID_OFFSET) { - fprintf(stderr, "Allocator was unable to find free space for the set."); - return; - } - - targetEntryCount = 2; - targetHeapDepth = true; - - D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; - dsvDesc.Format = toDXGI(desc.format); - - const bool isMSAA = (desc.multisampling.sampleCount > RenderSampleCount::COUNT_1); - switch (desc.dimension) { - case RenderTextureDimension::TEXTURE_1D: - dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE1D; - dsvDesc.Texture1D.MipSlice = 0; - break; - case RenderTextureDimension::TEXTURE_2D: - if (isMSAA) { - dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; - } - else { - dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; - dsvDesc.Texture2D.MipSlice = 0; - } - - break; - default: - assert(false && "Unsupported texture dimension for depth target."); - break; - } - - const D3D12_CPU_DESCRIPTOR_HANDLE writeHandle = device->depthTargetHeapAllocator->getCPUHandleAt(targetAllocatorOffset); - const D3D12_CPU_DESCRIPTOR_HANDLE readOnlyHandle = device->depthTargetHeapAllocator->getCPUHandleAt(targetAllocatorOffset + 1); - device->d3d->CreateDepthStencilView(d3d, &dsvDesc, writeHandle); - - dsvDesc.Flags = D3D12_DSV_FLAG_READ_ONLY_DEPTH; - device->d3d->CreateDepthStencilView(d3d, &dsvDesc, readOnlyHandle); - } - - void D3D12Texture::releaseTargetHeap() { - if (targetEntryCount > 0) { - if (targetHeapDepth) { - device->depthTargetHeapAllocator->free(targetAllocatorOffset, targetEntryCount); - } - else { - device->colorTargetHeapAllocator->free(targetAllocatorOffset, targetEntryCount); - } - - targetEntryCount = 0; - } - } - // D3D12AccelerationStructure D3D12AccelerationStructure::D3D12AccelerationStructure(D3D12Device *device, const RenderAccelerationStructureDesc &desc) { @@ -2672,11 +2851,12 @@ namespace plume { assert(format != RenderShaderFormat::UNKNOWN); assert(format == RenderShaderFormat::DXIL); - this->data = data; - this->size = size; this->device = device; this->format = format; this->entryPointName = (entryPointName != nullptr) ? std::string(entryPointName) : std::string(); + + const uint8_t *dataBytes = reinterpret_cast(data); + this->d3d = std::vector(dataBytes, dataBytes + size); } D3D12Shader::~D3D12Shader() { } @@ -2743,13 +2923,15 @@ namespace plume { D3D12ComputePipeline::D3D12ComputePipeline(D3D12Device *device, const RenderComputePipelineDesc &desc) : D3D12Pipeline(device, Type::Compute) { assert(desc.pipelineLayout != nullptr); + assert(desc.computeShader != nullptr); + assert((desc.threadGroupSizeX > 0) && (desc.threadGroupSizeY > 0) && (desc.threadGroupSizeZ > 0)); const D3D12PipelineLayout *rootSignature = static_cast(desc.pipelineLayout); const D3D12Shader *computeShader = static_cast(desc.computeShader); D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = {}; psoDesc.pRootSignature = rootSignature->rootSignature; - psoDesc.CS.pShaderBytecode = computeShader->data; - psoDesc.CS.BytecodeLength = computeShader->size; + psoDesc.CS.pShaderBytecode = computeShader->d3d.data(); + psoDesc.CS.BytecodeLength = computeShader->d3d.size(); device->d3d->CreateComputePipelineState(&psoDesc, IID_PPV_ARGS(&d3d)); } @@ -2781,12 +2963,12 @@ namespace plume { const D3D12Shader *pixelShader = static_cast(desc.pixelShader); D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; psoDesc.pRootSignature = pipelineLayout->rootSignature; - psoDesc.VS.pShaderBytecode = (vertexShader != nullptr) ? vertexShader->data : nullptr; - psoDesc.VS.BytecodeLength = (vertexShader != nullptr) ? vertexShader->size : 0; - psoDesc.GS.pShaderBytecode = (geometryShader != nullptr) ? geometryShader->data : nullptr; - psoDesc.GS.BytecodeLength = (geometryShader != nullptr) ? geometryShader->size : 0; - psoDesc.PS.pShaderBytecode = (pixelShader != nullptr) ? pixelShader->data : nullptr; - psoDesc.PS.BytecodeLength = (pixelShader != nullptr) ? pixelShader->size : 0; + psoDesc.VS.pShaderBytecode = (vertexShader != nullptr) ? vertexShader->d3d.data() : nullptr; + psoDesc.VS.BytecodeLength = (vertexShader != nullptr) ? vertexShader->d3d.size() : 0; + psoDesc.GS.pShaderBytecode = (geometryShader != nullptr) ? geometryShader->d3d.data() : nullptr; + psoDesc.GS.BytecodeLength = (geometryShader != nullptr) ? geometryShader->d3d.size() : 0; + psoDesc.PS.pShaderBytecode = (pixelShader != nullptr) ? pixelShader->d3d.data() : nullptr; + psoDesc.PS.BytecodeLength = (pixelShader != nullptr) ? pixelShader->d3d.size() : 0; psoDesc.SampleMask = UINT_MAX; psoDesc.SampleDesc.Count = desc.multisampling.sampleCount; if (desc.primitiveTopology == RenderPrimitiveTopology::LINE_STRIP || desc.primitiveTopology == RenderPrimitiveTopology::TRIANGLE_STRIP) { @@ -2796,10 +2978,15 @@ namespace plume { psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; psoDesc.RasterizerState.DepthClipEnable = desc.depthClipEnabled; psoDesc.RasterizerState.DepthBias = desc.depthBias; + psoDesc.RasterizerState.DepthBiasClamp = desc.depthBiasClamp; psoDesc.RasterizerState.SlopeScaledDepthBias = desc.slopeScaledDepthBias; if (desc.dynamicDepthBiasEnabled) { +# ifdef D3D12_AGILITY_SDK_ENABLED psoDesc.Flags |= D3D12_PIPELINE_STATE_FLAG_DYNAMIC_DEPTH_BIAS; +# else + assert(false && "Dynamic depth bias is unsupported without the Agility SDK."); +# endif } switch (desc.cullMode) { @@ -2940,8 +3127,8 @@ namespace plume { assert(libraryShader != nullptr); D3D12_DXIL_LIBRARY_DESC &libraryDesc = libraryDescs[i]; - libraryDesc.DXILLibrary.pShaderBytecode = libraryShader->data; - libraryDesc.DXILLibrary.BytecodeLength = libraryShader->size; + libraryDesc.DXILLibrary.pShaderBytecode = libraryShader->d3d.data(); + libraryDesc.DXILLibrary.BytecodeLength = libraryShader->d3d.size(); libraryDesc.pExports = &exportDescs[exportsIndexStart]; libraryDesc.NumExports = exportsIndex - exportsIndexStart; @@ -3383,8 +3570,11 @@ namespace plume { rtStateUpdateSupportOption = d3d12Options5.RaytracingTier >= D3D12_RAYTRACING_TIER_1_1; } - // Check if triangle fan is supported. bool triangleFanSupportOption = false; + bool dynamicDepthBiasOption = false; + +# ifdef D3D12_AGILITY_SDK_ENABLED + // Check if triangle fan is supported. D3D12_FEATURE_DATA_D3D12_OPTIONS15 d3d12Options15 = {}; res = deviceOption->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS15, &d3d12Options15, sizeof(d3d12Options15)); if (SUCCEEDED(res)) { @@ -3392,12 +3582,12 @@ namespace plume { } // Check if dynamic depth bias is supported. - bool dynamicDepthBiasOption = false; D3D12_FEATURE_DATA_D3D12_OPTIONS16 d3d12Options16 = {}; res = deviceOption->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS16, &d3d12Options16, sizeof(d3d12Options16)); if (SUCCEEDED(res)) { dynamicDepthBiasOption = d3d12Options16.DynamicDepthBiasSupported; } +# endif // Check if the architecture has UMA. bool uma = false; @@ -3425,10 +3615,11 @@ namespace plume { adapter = adapterOption; d3d = deviceOption; shaderModel = dataShaderModel.HighestShaderModel; - capabilities.geometryShader = true; capabilities.raytracing = rtSupportOption; capabilities.raytracingStateUpdate = rtStateUpdateSupportOption; capabilities.sampleLocations = samplePositionsOption; + // Resolve modes require sample positions support. + capabilities.resolveModes = samplePositionsOption; capabilities.triangleFan = triangleFanSupportOption; capabilities.dynamicDepthBias = dynamicDepthBiasOption; capabilities.uma = uma; @@ -3520,7 +3711,10 @@ namespace plume { // Fill capabilities. capabilities.descriptorIndexing = true; capabilities.scalarBlockLayout = true; + capabilities.bufferDeviceAddress = true; capabilities.presentWait = true; + capabilities.queryPools = true; + capabilities.maxTextureSize = 16384; capabilities.preferHDR = description.dedicatedVideoMemory > (512 * 1024 * 1024); // Create descriptor heaps allocator. @@ -3545,10 +3739,6 @@ namespace plume { release(); } - std::unique_ptr D3D12Device::createCommandList(RenderCommandListType type) { - return std::make_unique(this, type); - } - std::unique_ptr D3D12Device::createDescriptorSet(const RenderDescriptorSetDesc &desc) { return std::make_unique(this, desc); } @@ -3799,10 +3989,6 @@ namespace plume { return countsSupported; } - void D3D12Device::waitIdle() const { - assert(false && "Use fences to replicate wait idle behavior on D3D12."); - } - void D3D12Device::release() { if (d3d != nullptr) { d3d->Release(); @@ -3819,6 +4005,16 @@ namespace plume { return d3d != nullptr; } + bool D3D12Device::beginCapture() { + assert(false && "Captures are not currently implemented in D3D12."); + return false; + } + + bool D3D12Device::endCapture() { + assert(false && "Captures are not currently implemented in D3D12."); + return false; + } + // D3D12Interface D3D12Interface::D3D12Interface() { diff --git a/MarathonRecomp/gpu/rhi/plume_d3d12.h b/MarathonRecomp/gpu/rhi/plume_d3d12.h index d4987fbc9..0087486db 100644 --- a/MarathonRecomp/gpu/rhi/plume_d3d12.h +++ b/MarathonRecomp/gpu/rhi/plume_d3d12.h @@ -12,8 +12,14 @@ #include #include #include +#include + +#ifdef D3D12_AGILITY_SDK_ENABLED +# include +#else +# include +#endif -#include #include #include "D3D12MemAlloc.h" @@ -28,6 +34,7 @@ namespace plume { struct D3D12Pool; struct D3D12PipelineLayout; struct D3D12Texture; + struct D3D12TextureView; struct D3D12DescriptorHeapAllocator { enum : uint32_t { @@ -39,7 +46,6 @@ namespace plume { typedef std::map OffsetFreeBlockMap; typedef std::multimap SizeFreeBlockMap; - struct FreeBlock { uint32_t size; SizeFreeBlockMap::iterator sizeMapIterator; @@ -136,12 +142,17 @@ namespace plume { std::vector colorTargets; const D3D12Texture *depthTarget = nullptr; std::vector colorHandles; + std::vector colorTargetAllocatorOffsets; D3D12_CPU_DESCRIPTOR_HANDLE depthHandle = {}; + uint32_t depthTargetAllocatorOffset = 0; D3D12Framebuffer(D3D12Device *device, const RenderFramebufferDesc &desc); ~D3D12Framebuffer() override; uint32_t getWidth() const override; uint32_t getHeight() const override; + void createRenderTargetHeap(const D3D12Texture* texture, const D3D12TextureView* textureView); + void createDepthStencilHeap(const D3D12Texture* texture, const D3D12TextureView* textureView, bool readOnly); + void releaseTargetHeap(); }; struct D3D12QueryPool : RenderQueryPool { @@ -158,10 +169,13 @@ namespace plume { }; struct D3D12CommandList : RenderCommandList { +# ifdef D3D12_AGILITY_SDK_ENABLED ID3D12GraphicsCommandList9 *d3d = nullptr; +# else + ID3D12GraphicsCommandList7 *d3d = nullptr; +# endif ID3D12CommandAllocator *commandAllocator = nullptr; - D3D12Device *device = nullptr; - RenderCommandListType type = RenderCommandListType::UNKNOWN; + D3D12CommandQueue *queue = nullptr; const D3D12Framebuffer *targetFramebuffer = nullptr; bool targetFramebufferSamplePositionsSet = false; bool open = false; @@ -172,7 +186,7 @@ namespace plume { D3D12_PRIMITIVE_TOPOLOGY activeTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; bool activeSamplePositions = false; - D3D12CommandList(D3D12Device *device, RenderCommandListType type); + D3D12CommandList(D3D12CommandQueue *queue); ~D3D12CommandList() override; void begin() override; void end() override; @@ -248,6 +262,7 @@ namespace plume { D3D12CommandQueue(D3D12Device *device, RenderCommandListType type); ~D3D12CommandQueue() override; + std::unique_ptr createCommandList() override; std::unique_ptr createSwapChain(RenderWindow renderWindow, uint32_t textureCount, RenderFormat format, uint32_t newFrameLatency) override; void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) override; void waitForCommandFence(RenderCommandFence *fence) override; @@ -287,30 +302,32 @@ namespace plume { D3D12MA::Allocation *allocation = nullptr; D3D12Pool *pool = nullptr; RenderTextureDesc desc; - uint32_t targetAllocatorOffset = 0; - uint32_t targetEntryCount = 0; - bool targetHeapDepth = false; D3D12Texture() = default; D3D12Texture(D3D12Device *device, D3D12Pool *pool, const RenderTextureDesc &desc); ~D3D12Texture() override; std::unique_ptr createTextureView(const RenderTextureViewDesc &desc) override; void setName(const std::string &name) override; - void createRenderTargetHeap(); - void createDepthStencilHeap(); - void releaseTargetHeap(); }; struct D3D12TextureView : RenderTextureView { DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; D3D12Texture *texture = nullptr; + RenderTextureViewDesc desc; RenderTextureViewDimension dimension = RenderTextureViewDimension::UNKNOWN; uint32_t mipLevels = 0; uint32_t mipSlice = 0; + uint32_t arraySize = 0; + uint32_t arrayIndex = 0; uint32_t shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + uint32_t targetAllocatorOffset = 0; + uint32_t targetEntryCount = 0; + bool targetHeapDepth = false; D3D12TextureView(D3D12Texture *texture, const RenderTextureViewDesc &desc); ~D3D12TextureView() override; + + void createRenderTargetHeap(); }; struct D3D12AccelerationStructure :RenderAccelerationStructure { @@ -336,8 +353,7 @@ namespace plume { }; struct D3D12Shader : RenderShader { - const void* data = nullptr; - uint64_t size = 0; + std::vector d3d; std::string entryPointName; D3D12Device *device = nullptr; RenderShaderFormat format = RenderShaderFormat::UNKNOWN; @@ -436,7 +452,6 @@ namespace plume { D3D12Device(D3D12Interface *renderInterface, const std::string &preferredDeviceName); ~D3D12Device() override; - std::unique_ptr createCommandList(RenderCommandListType type) override; std::unique_ptr createDescriptorSet(const RenderDescriptorSetDesc &desc) override; std::unique_ptr createShader(const void *data, uint64_t size, const char *entryPointName, RenderShaderFormat format) override; std::unique_ptr createSampler(const RenderSamplerDesc &desc) override; @@ -459,9 +474,10 @@ namespace plume { const RenderDeviceCapabilities &getCapabilities() const override; const RenderDeviceDescription &getDescription() const override; RenderSampleCounts getSampleCountsSupported(RenderFormat format) const override; - void waitIdle() const override; void release(); bool isValid() const; + bool beginCapture() override; + bool endCapture() override; }; struct D3D12Interface : RenderInterface { diff --git a/MarathonRecomp/gpu/rhi/plume_render_interface.h b/MarathonRecomp/gpu/rhi/plume_render_interface.h index a608c9ece..9f55048e0 100644 --- a/MarathonRecomp/gpu/rhi/plume_render_interface.h +++ b/MarathonRecomp/gpu/rhi/plume_render_interface.h @@ -48,7 +48,6 @@ namespace plume { struct RenderShader { virtual ~RenderShader() { } - virtual void setName(const std::string &name) = 0; }; struct RenderSampler { @@ -195,13 +194,14 @@ namespace plume { struct RenderCommandQueue { virtual ~RenderCommandQueue() { } + virtual std::unique_ptr createCommandList() = 0; virtual std::unique_ptr createSwapChain(RenderWindow renderWindow, uint32_t textureCount, RenderFormat format, uint32_t maxFrameLatency) = 0; virtual void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores = nullptr, uint32_t waitSemaphoreCount = 0, RenderCommandSemaphore **signalSemaphores = nullptr, uint32_t signalSemaphoreCount = 0, RenderCommandFence *signalFence = nullptr) = 0; virtual void waitForCommandFence(RenderCommandFence *fence) = 0; // Concrete implementation shortcuts. inline void executeCommandLists(const RenderCommandList *commandList, RenderCommandFence *signalFence = nullptr) { - executeCommandLists(commandList != nullptr ? &commandList : nullptr, commandList != nullptr ? 1 : 0, nullptr, 0, nullptr, 0, signalFence); + executeCommandLists(&commandList, 1, nullptr, 0, nullptr, 0, signalFence); } }; @@ -220,7 +220,6 @@ namespace plume { struct RenderDevice { virtual ~RenderDevice() { } - virtual std::unique_ptr createCommandList(RenderCommandListType type) = 0; virtual std::unique_ptr createDescriptorSet(const RenderDescriptorSetDesc &desc) = 0; virtual std::unique_ptr createShader(const void *data, uint64_t size, const char *entryPointName, RenderShaderFormat format) = 0; virtual std::unique_ptr createSampler(const RenderSamplerDesc &desc) = 0; @@ -243,7 +242,8 @@ namespace plume { virtual const RenderDeviceCapabilities &getCapabilities() const = 0; virtual const RenderDeviceDescription &getDescription() const = 0; virtual RenderSampleCounts getSampleCountsSupported(RenderFormat format) const = 0; - virtual void waitIdle() const = 0; + virtual bool beginCapture() = 0; + virtual bool endCapture() = 0; }; struct RenderInterface { diff --git a/MarathonRecomp/gpu/rhi/plume_render_interface_types.h b/MarathonRecomp/gpu/rhi/plume_render_interface_types.h index 84914c889..f99b2c051 100644 --- a/MarathonRecomp/gpu/rhi/plume_render_interface_types.h +++ b/MarathonRecomp/gpu/rhi/plume_render_interface_types.h @@ -25,6 +25,8 @@ #undef LockMask #undef ControlMask #undef Success +#undef Always + #elif defined(__APPLE__) #include #endif @@ -52,11 +54,11 @@ namespace plume { }; #elif defined(__APPLE__) struct RenderWindow { - SDL_Window* window; + void* window; void* view; bool operator==(const struct RenderWindow& rhs) const { - return window == rhs.window; + return window == rhs.window && view == rhs.view; } bool operator!=(const struct RenderWindow& rhs) const { return !(*this == rhs); } }; @@ -71,6 +73,7 @@ namespace plume { struct RenderSampler; struct RenderShader; struct RenderTexture; + struct RenderTextureView; struct RenderQueryPool; // Enums. @@ -79,9 +82,10 @@ namespace plume { UNKNOWN = 0x0, AMD = 0x1002, NVIDIA = 0x10DE, - INTEL = 0x8086 + INTEL = 0x8086, + APPLE = 0x106B, }; - + enum class RenderFormat { UNKNOWN, R32G32B32A32_TYPELESS, @@ -156,7 +160,8 @@ namespace plume { BC6H_SF16, BC7_TYPELESS, BC7_UNORM, - BC7_UNORM_SRGB + BC7_UNORM_SRGB, + MAX }; enum class RenderTextureDimension { @@ -364,7 +369,8 @@ namespace plume { enum class RenderShaderFormat { UNKNOWN, DXIL, - SPIRV + SPIRV, + METAL }; enum class RenderRaytracingPipelineLibrarySymbolType { @@ -725,6 +731,14 @@ namespace plume { // Valid range is [-8, 7]. int8_t x = 0; int8_t y = 0; + + bool operator==(const RenderMultisamplingLocation& other) const { + return x == other.x && y == other.y; + } + + bool operator!=(const RenderMultisamplingLocation& other) const { + return !(*this == other); + } }; struct RenderMultisampling { @@ -885,9 +899,9 @@ namespace plume { RenderTextureDimension dimension = RenderTextureDimension::UNKNOWN; uint32_t width = 0; uint32_t height = 0; - uint16_t depth = 0; - uint16_t mipLevels = 0; - uint16_t arraySize = 0; + uint32_t depth = 0; + uint32_t mipLevels = 0; + uint32_t arraySize = 0; RenderMultisampling multisampling; RenderFormat format = RenderFormat::UNKNOWN; RenderTextureArrangement textureArrangement = RenderTextureArrangement::UNKNOWN; @@ -897,7 +911,7 @@ namespace plume { RenderTextureDesc() = default; - static RenderTextureDesc Texture(RenderTextureDimension dimension, uint32_t width, uint32_t height, uint16_t depth, uint16_t mipLevels, uint16_t arraySize, RenderFormat format, RenderTextureFlags flags = RenderTextureFlag::NONE) { + static RenderTextureDesc Texture(RenderTextureDimension dimension, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, uint32_t arraySize, RenderFormat format, RenderTextureFlags flags = RenderTextureFlag::NONE) { RenderTextureDesc desc; desc.dimension = dimension; desc.width = width; @@ -910,15 +924,15 @@ namespace plume { return desc; } - static RenderTextureDesc Texture1D(uint32_t width, uint16_t mipLevels, RenderFormat format, RenderTextureFlags flags = RenderTextureFlag::NONE) { + static RenderTextureDesc Texture1D(uint32_t width, uint32_t mipLevels, RenderFormat format, RenderTextureFlags flags = RenderTextureFlag::NONE) { return Texture(RenderTextureDimension::TEXTURE_1D, width, 1, 1, mipLevels, 1, format, flags); } - static RenderTextureDesc Texture2D(uint32_t width, uint32_t height, uint16_t mipLevels, RenderFormat format, RenderTextureFlags flags = RenderTextureFlag::NONE) { + static RenderTextureDesc Texture2D(uint32_t width, uint32_t height, uint32_t mipLevels, RenderFormat format, RenderTextureFlags flags = RenderTextureFlag::NONE) { return Texture(RenderTextureDimension::TEXTURE_2D, width, height, 1, mipLevels, 1, format, flags); } - static RenderTextureDesc Texture3D(uint32_t width, uint32_t height, uint32_t depth, uint16_t mipLevels, RenderFormat format, RenderTextureFlags flags = RenderTextureFlag::NONE) { + static RenderTextureDesc Texture3D(uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevels, RenderFormat format, RenderTextureFlags flags = RenderTextureFlag::NONE) { return Texture(RenderTextureDimension::TEXTURE_3D, width, height, depth, mipLevels, 1, format, flags); } @@ -980,41 +994,39 @@ namespace plume { struct RenderTextureViewDesc { RenderFormat format = RenderFormat::UNKNOWN; RenderTextureViewDimension dimension = RenderTextureViewDimension::UNKNOWN; - uint32_t mipLevels = 0; + uint32_t mipLevels = UINT32_MAX; uint32_t mipSlice = 0; + uint32_t arraySize = UINT32_MAX; + uint32_t arrayIndex = 0; RenderComponentMapping componentMapping; RenderTextureViewDesc() = default; - static RenderTextureViewDesc Texture1D(RenderFormat format, uint32_t mipLevels = 1) { + static RenderTextureViewDesc Texture1D(RenderFormat format) { RenderTextureViewDesc viewDesc; viewDesc.format = format; viewDesc.dimension = RenderTextureViewDimension::TEXTURE_1D; - viewDesc.mipLevels = mipLevels; return viewDesc; } - static RenderTextureViewDesc Texture2D(RenderFormat format, uint32_t mipLevels = 1) { + static RenderTextureViewDesc Texture2D(RenderFormat format) { RenderTextureViewDesc viewDesc; viewDesc.format = format; viewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; - viewDesc.mipLevels = mipLevels; return viewDesc; } - static RenderTextureViewDesc Texture3D(RenderFormat format, uint32_t mipLevels = 1) { + static RenderTextureViewDesc Texture3D(RenderFormat format) { RenderTextureViewDesc viewDesc; viewDesc.format = format; viewDesc.dimension = RenderTextureViewDimension::TEXTURE_3D; - viewDesc.mipLevels = mipLevels; return viewDesc; } - static RenderTextureViewDesc TextureCube(RenderFormat format, uint32_t mipLevels = 1) { + static RenderTextureViewDesc TextureCube(RenderFormat format) { RenderTextureViewDesc viewDesc; viewDesc.format = format; viewDesc.dimension = RenderTextureViewDimension::TEXTURE_CUBE; - viewDesc.mipLevels = mipLevels; return viewDesc; } }; @@ -1055,7 +1067,8 @@ namespace plume { } placedFootprint; struct { - uint32_t index; + uint32_t mipLevel; + uint32_t arrayIndex; } subresource; }; @@ -1072,11 +1085,12 @@ namespace plume { return loc; } - static RenderTextureCopyLocation Subresource(const RenderTexture *texture, uint32_t index = 0) { + static RenderTextureCopyLocation Subresource(const RenderTexture *texture, uint32_t mipLevel = 0, uint32_t arrayIndex = 0) { RenderTextureCopyLocation loc; loc.texture = texture; loc.type = RenderTextureCopyType::SUBRESOURCE; - loc.subresource.index = index; + loc.subresource.mipLevel = mipLevel; + loc.subresource.arrayIndex = arrayIndex; return loc; } }; @@ -1151,7 +1165,7 @@ namespace plume { desc.srcBlend = RenderBlend::SRC_ALPHA; desc.dstBlend = RenderBlend::INV_SRC_ALPHA; desc.blendOp = RenderBlendOperation::ADD; - desc.srcBlendAlpha = RenderBlend::SRC_ALPHA; + desc.srcBlendAlpha = RenderBlend::ONE; desc.dstBlendAlpha = RenderBlend::INV_SRC_ALPHA; desc.blendOpAlpha = RenderBlendOperation::ADD; return desc; @@ -1175,12 +1189,18 @@ namespace plume { const RenderShader *computeShader = nullptr; const RenderSpecConstant *specConstants = nullptr; uint32_t specConstantsCount = 0; + uint32_t threadGroupSizeX = 0; + uint32_t threadGroupSizeY = 0; + uint32_t threadGroupSizeZ = 0; RenderComputePipelineDesc() = default; - RenderComputePipelineDesc(const RenderPipelineLayout *pipelineLayout, const RenderShader *computeShader) { + RenderComputePipelineDesc(const RenderPipelineLayout *pipelineLayout, const RenderShader *computeShader, uint32_t threadGroupSizeX, uint32_t threadGroupSizeY, uint32_t threadGroupSizeZ) { this->pipelineLayout = pipelineLayout; this->computeShader = computeShader; + this->threadGroupSizeX = threadGroupSizeX; + this->threadGroupSizeY = threadGroupSizeY; + this->threadGroupSizeZ = threadGroupSizeZ; } }; @@ -1194,6 +1214,7 @@ namespace plume { RenderComparisonFunction depthFunction = RenderComparisonFunction::NEVER; bool depthClipEnabled = false; int32_t depthBias = 0; + float depthBiasClamp = 0.0f; float slopeScaledDepthBias = 0.0f; bool dynamicDepthBiasEnabled = false; bool depthEnabled = false; @@ -1293,9 +1314,9 @@ namespace plume { RenderFilter minFilter = RenderFilter::LINEAR; RenderFilter magFilter = RenderFilter::LINEAR; RenderMipmapMode mipmapMode = RenderMipmapMode::LINEAR; - RenderTextureAddressMode addressU = RenderTextureAddressMode::CLAMP; - RenderTextureAddressMode addressV = RenderTextureAddressMode::CLAMP; - RenderTextureAddressMode addressW = RenderTextureAddressMode::CLAMP; + RenderTextureAddressMode addressU = RenderTextureAddressMode::WRAP; + RenderTextureAddressMode addressV = RenderTextureAddressMode::WRAP; + RenderTextureAddressMode addressW = RenderTextureAddressMode::WRAP; float mipLODBias = 0.0f; uint32_t maxAnisotropy = 16; bool anisotropyEnabled = false; @@ -1641,8 +1662,10 @@ namespace plume { struct RenderFramebufferDesc { const RenderTexture **colorAttachments = nullptr; + const RenderTextureView **colorAttachmentViews = nullptr; uint32_t colorAttachmentsCount = 0; const RenderTexture *depthAttachment = nullptr; + const RenderTextureView *depthAttachmentView = nullptr; bool depthAttachmentReadOnly = false; RenderFramebufferDesc() = default; @@ -1795,14 +1818,23 @@ namespace plume { // MSAA. bool sampleLocations = false; + // Resolve Modes. + bool resolveModes = false; + // Bindless resources. bool descriptorIndexing = false; bool scalarBlockLayout = false; + // Buffers. + bool bufferDeviceAddress = false; + // Present. bool presentWait = false; bool displayTiming = false; + // Framebuffers. + uint64_t maxTextureSize = 0; + // HDR. bool preferHDR = false; @@ -1812,6 +1844,9 @@ namespace plume { // UMA. bool uma = false; + + // Query Pools. + bool queryPools = false; }; struct RenderInterfaceCapabilities { diff --git a/MarathonRecomp/gpu/rhi/plume_vulkan.cpp b/MarathonRecomp/gpu/rhi/plume_vulkan.cpp index 3f4c4a1e6..2fa315a2b 100644 --- a/MarathonRecomp/gpu/rhi/plume_vulkan.cpp +++ b/MarathonRecomp/gpu/rhi/plume_vulkan.cpp @@ -14,14 +14,13 @@ #include #include #include -#include #if DLSS_ENABLED # include "render/plume_dlss.h" #endif #ifndef NDEBUG -// # define VULKAN_VALIDATION_LAYER_ENABLED +# define VULKAN_VALIDATION_LAYER_ENABLED //# define VULKAN_OBJECT_NAMES_ENABLED #endif @@ -46,9 +45,6 @@ namespace plume { static const std::unordered_set RequiredInstanceExtensions = { VK_KHR_SURFACE_EXTENSION_NAME, -# ifdef VULKAN_OBJECT_NAMES_ENABLED - VK_EXT_DEBUG_UTILS_EXTENSION_NAME, -# endif # if defined(_WIN64) VK_KHR_WIN32_SURFACE_EXTENSION_NAME, # elif defined(__ANDROID__) @@ -66,13 +62,16 @@ namespace plume { VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, # endif }; - + static const std::unordered_set RequiredDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME, +# ifdef VULKAN_OBJECT_NAMES_ENABLED + VK_EXT_DEBUG_UTILS_EXTENSION_NAME +# endif }; static const std::unordered_set OptionalDeviceExtensions = { @@ -582,13 +581,14 @@ namespace plume { flags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; - if (geometrySupported) { - flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; - } flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; flags |= VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + + if (geometrySupported) { + flags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + } } if (stages & RenderBarrierStage::COMPUTE) { @@ -730,16 +730,16 @@ namespace plume { } } - static void setObjectName(VkDevice device, VkObjectType objectType, uint64_t object, const std::string &name) { + static void setObjectName(VkDevice device, VkDebugReportObjectTypeEXT objectType, uint64_t object, const std::string &name) { # ifdef VULKAN_OBJECT_NAMES_ENABLED - VkDebugUtilsObjectNameInfoEXT nameInfo = {}; - nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; + VkDebugMarkerObjectNameInfoEXT nameInfo = {}; + nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT; nameInfo.objectType = objectType; - nameInfo.objectHandle = object; + nameInfo.object = object; nameInfo.pObjectName = name.c_str(); - VkResult res = vkSetDebugUtilsObjectNameEXT(device, &nameInfo); + VkResult res = vkDebugMarkerSetObjectNameEXT(device, &nameInfo); if (res != VK_SUCCESS) { - fprintf(stderr, "vkSetDebugUtilsObjectNameEXT failed with error code 0x%X.\n", res); + fprintf(stderr, "vkDebugMarkerSetObjectNameEXT failed with error code 0x%X.\n", res); return; } # endif @@ -874,7 +874,7 @@ namespace plume { } void VulkanBuffer::setName(const std::string &name) { - setObjectName(device->vk, VK_OBJECT_TYPE_IMAGE, uint64_t(vk), name); + setObjectName(device->vk, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, uint64_t(vk), name); } uint64_t VulkanBuffer::getDeviceAddress() const { @@ -1012,28 +1012,33 @@ namespace plume { } void VulkanTexture::setName(const std::string &name) { - setObjectName(device->vk, VK_OBJECT_TYPE_IMAGE, uint64_t(vk), name); + setObjectName(device->vk, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, uint64_t(vk), name); } - + void VulkanTexture::fillSubresourceRange() { imageSubresourceRange.aspectMask = (desc.flags & RenderTextureFlag::DEPTH_TARGET) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; imageSubresourceRange.baseMipLevel = 0; imageSubresourceRange.levelCount = desc.mipLevels; imageSubresourceRange.baseArrayLayer = 0; - imageSubresourceRange.layerCount = 1; + imageSubresourceRange.layerCount = desc.arraySize; } // VulkanTextureView VulkanTextureView::VulkanTextureView(VulkanTexture *texture, const RenderTextureViewDesc &desc) { assert(texture != nullptr); + assert(desc.mipSlice < texture->desc.mipLevels); + assert(desc.arrayIndex < texture->desc.arraySize); this->texture = texture; + this->desc = desc; + + const uint32_t layerCount = std::min(desc.arraySize, texture->desc.arraySize - desc.arrayIndex); VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.image = texture->vk; - viewInfo.viewType = toImageViewType(desc.dimension, texture->desc.arraySize); + viewInfo.viewType = toImageViewType(desc.dimension, layerCount); viewInfo.format = toVk(desc.format); viewInfo.components.r = toVk(desc.componentMapping.r); viewInfo.components.g = toVk(desc.componentMapping.g); @@ -1041,9 +1046,9 @@ namespace plume { viewInfo.components.a = toVk(desc.componentMapping.a); viewInfo.subresourceRange.aspectMask = (texture->desc.flags & RenderTextureFlag::DEPTH_TARGET) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.subresourceRange.baseMipLevel = desc.mipSlice; - viewInfo.subresourceRange.levelCount = desc.mipLevels; - viewInfo.subresourceRange.baseArrayLayer = 0; - viewInfo.subresourceRange.layerCount = texture->desc.arraySize; + viewInfo.subresourceRange.levelCount = std::min(desc.mipLevels, texture->desc.mipLevels - desc.mipSlice); + viewInfo.subresourceRange.baseArrayLayer = desc.arrayIndex; + viewInfo.subresourceRange.layerCount = layerCount; VkResult res = vkCreateImageView(texture->device->vk, &viewInfo, nullptr, &vk); if (res != VK_SUCCESS) { @@ -1256,10 +1261,6 @@ namespace plume { } } - void VulkanShader::setName(const std::string &name) { - setObjectName(device->vk, VK_OBJECT_TYPE_SHADER_MODULE, uint64_t(vk), name); - } - // VulkanSampler VulkanSampler::VulkanSampler(VulkanDevice *device, const RenderSamplerDesc &desc) { @@ -1315,6 +1316,7 @@ namespace plume { VulkanComputePipeline::VulkanComputePipeline(VulkanDevice *device, const RenderComputePipelineDesc &desc) : VulkanPipeline(device, Type::Compute) { assert(desc.computeShader != nullptr); assert(desc.pipelineLayout != nullptr); + assert((desc.threadGroupSizeX > 0) && (desc.threadGroupSizeY > 0) && (desc.threadGroupSizeZ > 0)); std::vector specEntries(desc.specConstantsCount); std::vector specData(desc.specConstantsCount); @@ -1349,7 +1351,7 @@ namespace plume { } void VulkanComputePipeline::setName(const std::string& name) const { - setObjectName(device->vk, VK_OBJECT_TYPE_PIPELINE, uint64_t(vk), name); + setObjectName(device->vk, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, uint64_t(vk), name); } RenderPipelineProgram VulkanComputePipeline::getProgram(const std::string &name) const { @@ -1464,9 +1466,10 @@ namespace plume { if (desc.dynamicDepthBiasEnabled) { rasterization.depthBiasEnable = true; } - else if (desc.depthBias != 0 || desc.slopeScaledDepthBias != 0.0f) { + else if ((desc.depthBias != 0) || (desc.depthBiasClamp != 0.0f) || (desc.slopeScaledDepthBias != 0.0f)) { rasterization.depthBiasEnable = true; rasterization.depthBiasConstantFactor = float(desc.depthBias); + rasterization.depthBiasClamp = desc.depthBiasClamp; rasterization.depthBiasSlopeFactor = desc.slopeScaledDepthBias; } @@ -1596,7 +1599,7 @@ namespace plume { } void VulkanGraphicsPipeline::setName(const std::string& name) const { - setObjectName(device->vk, VK_OBJECT_TYPE_PIPELINE, uint64_t(vk), name); + setObjectName(device->vk, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, uint64_t(vk), name); } RenderPipelineProgram VulkanGraphicsPipeline::getProgram(const std::string &name) const { @@ -1800,7 +1803,7 @@ namespace plume { } void VulkanRaytracingPipeline::setName(const std::string& name) const { - setObjectName(device->vk, VK_OBJECT_TYPE_PIPELINE, uint64_t(vk), name); + setObjectName(device->vk, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, uint64_t(vk), name); } RenderPipelineProgram VulkanRaytracingPipeline::getProgram(const std::string &name) const { @@ -2062,6 +2065,9 @@ namespace plume { # elif defined(__APPLE__) assert(renderWindow.window != 0); assert(renderWindow.view != 0); + // Creates a wrapper around the window for storing and fetching sizes. + this->windowWrapper = std::make_unique(renderWindow.window); + VkMetalSurfaceCreateInfoEXT surfaceCreateInfo = {}; surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; surfaceCreateInfo.pLayer = renderWindow.view; @@ -2211,7 +2217,6 @@ namespace plume { res = vkQueuePresentKHR(commandQueue->queue->vk, &presentInfo); } - // Handle the error silently. #if defined(__APPLE__) // Under MoltenVK, VK_SUBOPTIMAL_KHR does not result in a valid state for rendering. We intentionally // only check for this error during present to avoid having to synchronize manually against the semaphore @@ -2389,7 +2394,10 @@ namespace plume { dstWidth = attributes.width; dstHeight = attributes.height; # elif defined(__APPLE__) - SDL_GetWindowSizeInPixels(renderWindow.window, (int *)(&dstWidth), (int *)(&dstHeight)); + CocoaWindowAttributes attributes; + windowWrapper->getWindowAttributes(&attributes); + dstWidth = attributes.width; + dstHeight = attributes.height; # endif } @@ -2435,10 +2443,22 @@ namespace plume { std::vector imageViews; VkAttachmentReference depthReference = {}; for (uint32_t i = 0; i < desc.colorAttachmentsCount; i++) { - const VulkanTexture *colorAttachment = static_cast(desc.colorAttachments[i]); + const VulkanTexture *colorAttachment; + VkImageView colorAttachmentImageView; + RenderFormat colorAttachmentFormat; + if (desc.colorAttachmentViews && desc.colorAttachmentViews[i]) { + const VulkanTextureView* colorAttachmentView = static_cast(desc.colorAttachmentViews[i]); + colorAttachment = colorAttachmentView->texture; + colorAttachmentImageView = colorAttachmentView->vk; + colorAttachmentFormat = colorAttachmentView->desc.format; + } else { + colorAttachment = static_cast(desc.colorAttachments[i]); + colorAttachmentImageView = colorAttachment->imageView; + colorAttachmentFormat = colorAttachment->desc.format; + } assert((colorAttachment->desc.flags & RenderTextureFlag::RENDER_TARGET) && "Color attachment must be a render target."); colorAttachments.emplace_back(colorAttachment); - imageViews.emplace_back(colorAttachment->imageView); + imageViews.emplace_back(colorAttachmentImageView); if (i == 0) { width = uint32_t(colorAttachment->desc.width); @@ -2451,7 +2471,7 @@ namespace plume { colorReferences.emplace_back(reference); VkAttachmentDescription attachment = {}; - attachment.format = toVk(colorAttachment->desc.format); + attachment.format = toVk(colorAttachmentFormat); attachment.samples = VkSampleCountFlagBits(colorAttachment->desc.multisampling.sampleCount); attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -2462,10 +2482,21 @@ namespace plume { attachments.emplace_back(attachment); } - if (desc.depthAttachment != nullptr) { - depthAttachment = static_cast(desc.depthAttachment); + if (desc.depthAttachment != nullptr || desc.depthAttachmentView != nullptr) { + VkImageView depthAttachmentImageView; + RenderFormat depthAttachmentFormat; + if (desc.depthAttachmentView != nullptr) { + const VulkanTextureView* depthAttachmentView = static_cast(desc.depthAttachmentView); + depthAttachment = depthAttachmentView->texture; + depthAttachmentImageView = depthAttachmentView->vk; + depthAttachmentFormat = depthAttachmentView->desc.format; + } else { + depthAttachment = static_cast(desc.depthAttachment); + depthAttachmentImageView = depthAttachment->imageView; + depthAttachmentFormat = depthAttachment->desc.format; + } assert((depthAttachment->desc.flags & RenderTextureFlag::DEPTH_TARGET) && "Depth attachment must be a depth target."); - imageViews.emplace_back(depthAttachment->imageView); + imageViews.emplace_back(depthAttachmentImageView); if (desc.colorAttachmentsCount == 0) { width = uint32_t(depthAttachment->desc.width); @@ -2479,7 +2510,7 @@ namespace plume { // We prefer to just ignore this potential hazard on older Vulkan versions as it just seems to be an edge case for some hardware. const bool preferNoneForReadOnly = desc.depthAttachmentReadOnly && device->loadStoreOpNoneSupported; VkAttachmentDescription attachment = {}; - attachment.format = toVk(depthAttachment->desc.format); + attachment.format = toVk(depthAttachmentFormat); attachment.samples = VkSampleCountFlagBits(depthAttachment->desc.multisampling.sampleCount); attachment.loadOp = preferNoneForReadOnly ? VK_ATTACHMENT_LOAD_OP_NONE_EXT : VK_ATTACHMENT_LOAD_OP_LOAD; attachment.storeOp = preferNoneForReadOnly ? VK_ATTACHMENT_STORE_OP_NONE_EXT : VK_ATTACHMENT_STORE_OP_STORE; @@ -2634,19 +2665,17 @@ namespace plume { // VulkanCommandList - VulkanCommandList::VulkanCommandList(VulkanDevice *device, RenderCommandListType type) { - assert(device != nullptr); - assert(type != RenderCommandListType::UNKNOWN); + VulkanCommandList::VulkanCommandList(VulkanCommandQueue *queue) { + assert(queue != nullptr); - this->device = device; - this->type = type; + this->queue = queue; VkCommandPoolCreateInfo poolInfo = {}; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = device->queueFamilyIndices[toFamilyIndex(type)]; + poolInfo.queueFamilyIndex = queue->device->queueFamilyIndices[toFamilyIndex(queue->type)]; - VkResult res = vkCreateCommandPool(device->vk, &poolInfo, nullptr, &commandPool); + VkResult res = vkCreateCommandPool(queue->device->vk, &poolInfo, nullptr, &commandPool); if (res != VK_SUCCESS) { fprintf(stderr, "vkCreateCommandPool failed with error code 0x%X.\n", res); return; @@ -2658,7 +2687,7 @@ namespace plume { allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocateInfo.commandBufferCount = 1; - res = vkAllocateCommandBuffers(device->vk, &allocateInfo, &vk); + res = vkAllocateCommandBuffers(queue->device->vk, &allocateInfo, &vk); if (res != VK_SUCCESS) { fprintf(stderr, "vkAllocateCommandBuffers failed with error code 0x%X.\n", res); return; @@ -2667,11 +2696,11 @@ namespace plume { VulkanCommandList::~VulkanCommandList() { if (vk != VK_NULL_HANDLE) { - vkFreeCommandBuffers(device->vk, commandPool, 1, &vk); + vkFreeCommandBuffers(queue->device->vk, commandPool, 1, &vk); } if (commandPool != VK_NULL_HANDLE) { - vkDestroyCommandPool(device->vk, commandPool, nullptr); + vkDestroyCommandPool(queue->device->vk, commandPool, nullptr); } } @@ -2713,8 +2742,8 @@ namespace plume { endActiveRenderPass(); - const bool geometryEnabled = device->capabilities.geometryShader; - const bool rtEnabled = device->capabilities.raytracing; + const bool geometryEnabled = queue->device->capabilities.geometryShader; + const bool rtEnabled = queue->device->capabilities.raytracing; VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT | toStageFlags(stages, geometryEnabled, rtEnabled); thread_local std::vector bufferMemoryBarriers; @@ -2780,7 +2809,7 @@ namespace plume { tableAddressInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; tableAddressInfo.buffer = interfaceBuffer->vk; - const VkDeviceAddress tableAddress = vkGetBufferDeviceAddress(device->vk, &tableAddressInfo) + shaderBindingTable.offset; + const VkDeviceAddress tableAddress = vkGetBufferDeviceAddress(queue->device->vk, &tableAddressInfo) + shaderBindingTable.offset; const RenderShaderBindingGroupInfo &rayGen = shaderBindingGroupsInfo.rayGen; const RenderShaderBindingGroupInfo &miss = shaderBindingGroupsInfo.miss; const RenderShaderBindingGroupInfo &hitGroup = shaderBindingGroupsInfo.hitGroup; @@ -2921,9 +2950,10 @@ namespace plume { offsetVector.clear(); for (uint32_t i = 0; i < viewCount; i++) { const VulkanBuffer *interfaceBuffer = static_cast(views[i].buffer.ref); - if (interfaceBuffer == nullptr && !device->nullDescriptorSupported) { - interfaceBuffer = static_cast(device->nullBuffer.get()); + if ((interfaceBuffer == nullptr) && !queue->device->nullDescriptorSupported) { + interfaceBuffer = static_cast(queue->device->nullBuffer.get()); } + bufferVector.emplace_back((interfaceBuffer != nullptr) ? interfaceBuffer->vk : VK_NULL_HANDLE); offsetVector.emplace_back(views[i].buffer.offset); } @@ -3090,9 +3120,9 @@ namespace plume { imageCopy.bufferRowLength = ((srcLocation.placedFootprint.rowWidth + blockWidth - 1) / blockWidth) * blockWidth; imageCopy.bufferImageHeight = ((srcLocation.placedFootprint.height + blockWidth - 1) / blockWidth) * blockWidth; imageCopy.imageSubresource.aspectMask = (dstTexture->desc.flags & RenderTextureFlag::DEPTH_TARGET) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - imageCopy.imageSubresource.baseArrayLayer = dstLocation.subresource.index / dstTexture->desc.mipLevels; + imageCopy.imageSubresource.baseArrayLayer = dstLocation.subresource.arrayIndex; imageCopy.imageSubresource.layerCount = 1; - imageCopy.imageSubresource.mipLevel = dstLocation.subresource.index % dstTexture->desc.mipLevels; + imageCopy.imageSubresource.mipLevel = dstLocation.subresource.mipLevel; imageCopy.imageOffset.x = dstX; imageCopy.imageOffset.y = dstY; imageCopy.imageOffset.z = dstZ; @@ -3104,13 +3134,13 @@ namespace plume { else { VkImageCopy imageCopy = {}; imageCopy.srcSubresource.aspectMask = (srcTexture->desc.flags & RenderTextureFlag::DEPTH_TARGET) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - imageCopy.srcSubresource.baseArrayLayer = 0; + imageCopy.srcSubresource.baseArrayLayer = srcLocation.subresource.arrayIndex; imageCopy.srcSubresource.layerCount = 1; - imageCopy.srcSubresource.mipLevel = srcLocation.subresource.index; + imageCopy.srcSubresource.mipLevel = srcLocation.subresource.mipLevel; imageCopy.dstSubresource.aspectMask = (dstTexture->desc.flags & RenderTextureFlag::DEPTH_TARGET) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - imageCopy.dstSubresource.baseArrayLayer = 0; + imageCopy.dstSubresource.baseArrayLayer = dstLocation.subresource.arrayIndex; imageCopy.dstSubresource.layerCount = 1; - imageCopy.dstSubresource.mipLevel = dstLocation.subresource.index; + imageCopy.dstSubresource.mipLevel = dstLocation.subresource.mipLevel; imageCopy.dstOffset.x = dstX; imageCopy.dstOffset.y = dstY; imageCopy.dstOffset.z = dstZ; @@ -3257,7 +3287,7 @@ namespace plume { buildGeometryInfo.flags = toRTASBuildFlags(buildInfo.preferFastBuild, buildInfo.preferFastTrace); buildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; buildGeometryInfo.dstAccelerationStructure = interfaceAccelerationStructure->vk; - buildGeometryInfo.scratchData.deviceAddress = vkGetBufferDeviceAddress(device->vk, &scratchAddressInfo) + scratchBuffer.offset; + buildGeometryInfo.scratchData.deviceAddress = vkGetBufferDeviceAddress(queue->device->vk, &scratchAddressInfo) + scratchBuffer.offset; buildGeometryInfo.pGeometries = reinterpret_cast(buildInfo.buildData.data()); buildGeometryInfo.geometryCount = buildInfo.meshCount; @@ -3299,14 +3329,14 @@ namespace plume { VkAccelerationStructureGeometryInstancesDataKHR &instancesData = topGeometry.geometry.instances; instancesData.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR; - instancesData.data.deviceAddress = vkGetBufferDeviceAddress(device->vk, &instancesAddressInfo) + instancesBuffer.offset; + instancesData.data.deviceAddress = vkGetBufferDeviceAddress(queue->device->vk, &instancesAddressInfo) + instancesBuffer.offset; VkAccelerationStructureBuildGeometryInfoKHR buildGeometryInfo = {}; buildGeometryInfo.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; buildGeometryInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; buildGeometryInfo.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; buildGeometryInfo.dstAccelerationStructure = interfaceAccelerationStructure->vk; - buildGeometryInfo.scratchData.deviceAddress = vkGetBufferDeviceAddress(device->vk, &scratchAddressInfo) + scratchBuffer.offset; + buildGeometryInfo.scratchData.deviceAddress = vkGetBufferDeviceAddress(queue->device->vk, &scratchAddressInfo) + scratchBuffer.offset; buildGeometryInfo.pGeometries = &topGeometry; buildGeometryInfo.geometryCount = 1; @@ -3417,13 +3447,14 @@ namespace plume { // VulkanCommandQueue - VulkanCommandQueue::VulkanCommandQueue(VulkanDevice *device, RenderCommandListType commandListType) { + VulkanCommandQueue::VulkanCommandQueue(VulkanDevice *device, RenderCommandListType type) { assert(device != nullptr); - assert(commandListType != RenderCommandListType::UNKNOWN); + assert(type != RenderCommandListType::UNKNOWN); this->device = device; + this->type = type; - familyIndex = device->queueFamilyIndices[toFamilyIndex(commandListType)]; + familyIndex = device->queueFamilyIndices[toFamilyIndex(type)]; device->queueFamilies[familyIndex].add(this); } @@ -3431,6 +3462,10 @@ namespace plume { device->queueFamilies[familyIndex].remove(this); } + std::unique_ptr VulkanCommandQueue::createCommandList() { + return std::make_unique(this); + } + std::unique_ptr VulkanCommandQueue::createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format, uint32_t maxFrameLatency) { return std::make_unique(this, renderWindow, bufferCount, format, maxFrameLatency); } @@ -3956,24 +3991,29 @@ namespace plume { capabilities.raytracing = rtSupported; capabilities.raytracingStateUpdate = false; capabilities.sampleLocations = sampleLocationsSupported; + capabilities.resolveModes = false; capabilities.descriptorIndexing = descriptorIndexing; capabilities.scalarBlockLayout = scalarBlockLayout; + capabilities.bufferDeviceAddress = bufferDeviceAddress; capabilities.presentWait = presentWait; capabilities.displayTiming = supportedOptionalExtensions.find(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME) != supportedOptionalExtensions.end(); + capabilities.maxTextureSize = physicalDeviceProperties.limits.maxImageDimension2D; capabilities.preferHDR = memoryHeapSize > (512 * 1024 * 1024); -#if defined(__APPLE__) - // MoltenVK supports triangle fans but does so via compute shaders to translate to lists, since it has to - // support all cases including indirect draw. This results in renderpass restarts that can harm performance, - // so force disable native triangle fan support and rely on the game to emulate fans if needed. - capabilities.triangleFan = false; - #else - capabilities.triangleFan = true; - #endif - capabilities.dynamicDepthBias = true; + capabilities.queryPools = true; + +# if defined(__APPLE__) + // MoltenVK supports triangle fans but does so via compute shaders to translate to lists, since it has to + // support all cases including indirect draw. This results in renderpass restarts that can harm performance, + // so force disable native triangle fan support and rely on the game to emulate fans if needed. + capabilities.triangleFan = false; +# else + capabilities.triangleFan = true; +# endif // Fill Vulkan-only capabilities. loadStoreOpNoneSupported = supportedOptionalExtensions.find(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME) != supportedOptionalExtensions.end(); + nullDescriptorSupported = nullDescriptor; if (!nullDescriptorSupported) { nullBuffer = createBuffer(RenderBufferDesc::DefaultBuffer(16, RenderBufferFlag::VERTEX)); @@ -3984,10 +4024,6 @@ namespace plume { release(); } - std::unique_ptr VulkanDevice::createCommandList(RenderCommandListType type) { - return std::make_unique(this, type); - } - std::unique_ptr VulkanDevice::createDescriptorSet(const RenderDescriptorSetDesc &desc) { return std::make_unique(this, desc); } @@ -4258,10 +4294,6 @@ namespace plume { } } - void VulkanDevice::waitIdle() const { - vkDeviceWaitIdle(vk); - } - void VulkanDevice::release() { if (allocator != VK_NULL_HANDLE) { vmaDestroyAllocator(allocator); @@ -4278,6 +4310,16 @@ namespace plume { return vk != nullptr; } + bool VulkanDevice::beginCapture() { + assert(false && "Captures are not currently implemented in Vulkan."); + return false; + } + + bool VulkanDevice::endCapture() { + assert(false && "Captures are not currently implemented in Vulkan."); + return false; + } + // VulkanInterface #if SDL_VULKAN_ENABLED diff --git a/MarathonRecomp/gpu/rhi/plume_vulkan.h b/MarathonRecomp/gpu/rhi/plume_vulkan.h index a01a5bd2c..668d85700 100644 --- a/MarathonRecomp/gpu/rhi/plume_vulkan.h +++ b/MarathonRecomp/gpu/rhi/plume_vulkan.h @@ -22,6 +22,7 @@ #define VK_USE_PLATFORM_XLIB_KHR #elif defined(__APPLE__) #define VK_USE_PLATFORM_METAL_EXT +#include "plume_apple.h" #endif // For VK_KHR_portability_subset @@ -101,6 +102,7 @@ namespace plume { struct VulkanTextureView : RenderTextureView { VkImageView vk = VK_NULL_HANDLE; VulkanTexture *texture = nullptr; + RenderTextureViewDesc desc; VulkanTextureView(VulkanTexture *texture, const RenderTextureViewDesc &desc); ~VulkanTextureView() override; @@ -144,7 +146,6 @@ namespace plume { VulkanShader(VulkanDevice *device, const void *data, uint64_t size, const char *entryPointName, RenderShaderFormat format); ~VulkanShader() override; - void setName(const std::string &name) override; }; struct VulkanSampler : RenderSampler { @@ -224,6 +225,9 @@ namespace plume { VulkanCommandQueue *commandQueue = nullptr; VkSurfaceKHR surface = VK_NULL_HANDLE; RenderWindow renderWindow = {}; +#if defined(__APPLE__) + std::unique_ptr windowWrapper; +#endif uint32_t textureCount = 0; uint64_t presentCount = 0; RenderFormat format = RenderFormat::UNKNOWN; @@ -292,15 +296,14 @@ namespace plume { struct VulkanCommandList : RenderCommandList { VkCommandBuffer vk = VK_NULL_HANDLE; VkCommandPool commandPool = VK_NULL_HANDLE; - VulkanDevice *device = nullptr; - RenderCommandListType type = RenderCommandListType::UNKNOWN; + VulkanCommandQueue *queue = nullptr; const VulkanFramebuffer *targetFramebuffer = nullptr; const VulkanPipelineLayout *activeComputePipelineLayout = nullptr; const VulkanPipelineLayout *activeGraphicsPipelineLayout = nullptr; const VulkanPipelineLayout *activeRaytracingPipelineLayout = nullptr; VkRenderPass activeRenderPass = VK_NULL_HANDLE; - VulkanCommandList(VulkanDevice *device, RenderCommandListType type); + VulkanCommandList(VulkanCommandQueue *queue); ~VulkanCommandList() override; void begin() override; void end() override; @@ -366,9 +369,11 @@ namespace plume { uint32_t familyIndex = 0; uint32_t queueIndex = 0; std::unordered_set swapChains; + RenderCommandListType type = RenderCommandListType::UNKNOWN; - VulkanCommandQueue(VulkanDevice *device, RenderCommandListType commandListType); + VulkanCommandQueue(VulkanDevice *device, RenderCommandListType type); ~VulkanCommandQueue() override; + std::unique_ptr createCommandList() override; std::unique_ptr createSwapChain(RenderWindow renderWindow, uint32_t bufferCount, RenderFormat format, uint32_t maxFrameLatency) override; void executeCommandLists(const RenderCommandList **commandLists, uint32_t commandListCount, RenderCommandSemaphore **waitSemaphores, uint32_t waitSemaphoreCount, RenderCommandSemaphore **signalSemaphores, uint32_t signalSemaphoreCount, RenderCommandFence *signalFence) override; void waitForCommandFence(RenderCommandFence *fence) override; @@ -409,13 +414,12 @@ namespace plume { RenderDeviceDescription description; VkPhysicalDeviceRayTracingPipelinePropertiesKHR rtPipelineProperties = {}; VkPhysicalDeviceSampleLocationsPropertiesEXT sampleLocationProperties = {}; - std::unique_ptr nullBuffer = nullptr; + std::unique_ptr nullBuffer; bool loadStoreOpNoneSupported = false; bool nullDescriptorSupported = false; VulkanDevice(VulkanInterface *renderInterface, const std::string &preferredDeviceName); ~VulkanDevice() override; - std::unique_ptr createCommandList(RenderCommandListType type) override; std::unique_ptr createDescriptorSet(const RenderDescriptorSetDesc &desc) override; std::unique_ptr createShader(const void *data, uint64_t size, const char *entryPointName, RenderShaderFormat format) override; std::unique_ptr createSampler(const RenderSamplerDesc &desc) override; @@ -438,9 +442,10 @@ namespace plume { const RenderDeviceCapabilities &getCapabilities() const override; const RenderDeviceDescription &getDescription() const override; RenderSampleCounts getSampleCountsSupported(RenderFormat format) const override; - void waitIdle() const override; void release(); bool isValid() const; + bool beginCapture() override; + bool endCapture() override; }; struct VulkanInterface : RenderInterface { diff --git a/MarathonRecomp/gpu/video.cpp b/MarathonRecomp/gpu/video.cpp index eb2fea97f..c6c1bf34c 100644 --- a/MarathonRecomp/gpu/video.cpp +++ b/MarathonRecomp/gpu/video.cpp @@ -872,6 +872,7 @@ struct RenderCommand GuestDevice* device; uint32_t flags; GuestTexture* texture; + uint32_t destSliceOrFace; } stretchRect; struct @@ -1751,7 +1752,7 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver) g_queue = g_device->createCommandQueue(RenderCommandListType::DIRECT); for (auto& commandList : g_commandLists) - commandList = g_device->createCommandList(RenderCommandListType::DIRECT); + commandList = g_queue->createCommandList(); for (auto& commandFence : g_commandFences) commandFence = g_device->createCommandFence(); @@ -1760,7 +1761,7 @@ bool Video::CreateHostDevice(const char *sdlVideoDriver) queryPool = g_device->createQueryPool(NUM_QUERIES); g_copyQueue = g_device->createCommandQueue(RenderCommandListType::COPY); - g_copyCommandList = g_device->createCommandList(RenderCommandListType::COPY); + g_copyCommandList = g_copyQueue->createCommandList(); g_copyCommandFence = g_device->createCommandFence(); uint32_t bufferCount = 2; @@ -1985,23 +1986,21 @@ void Video::WaitForGPU() { g_waitForGPUCount++; - if (g_vulkan) - { - g_device->waitIdle(); - } - else + // Wait for all queued frames to finish. + for (size_t i = 0; i < NUM_FRAMES; i++) { - for (size_t i = 0; i < NUM_FRAMES; i++) + if (g_commandListStates[i]) { - if (g_commandListStates[i]) - { - g_queue->waitForCommandFence(g_commandFences[i].get()); - g_commandListStates[i] = false; - } + g_queue->waitForCommandFence(g_commandFences[i].get()); + g_commandListStates[i] = false; } - g_queue->executeCommandLists(nullptr, g_commandFences[0].get()); - g_queue->waitForCommandFence(g_commandFences[0].get()); } + + // Execute an empty command list and wait for it to end to guarantee that any remaining presentation has finished. + g_commandLists[0]->begin(); + g_commandLists[0]->end(); + g_queue->executeCommandLists(g_commandLists[0].get(), g_commandFences[0].get()); + g_queue->waitForCommandFence(g_commandFences[0].get()); } static uint32_t getSetAddress(uint32_t base, int index) { @@ -3085,6 +3084,7 @@ static GuestTexture* CreateTexture(uint32_t width, uint32_t height, uint32_t dep texture->height = height; texture->depth = depth; texture->format = desc.format; + texture->mipLevels = viewDesc.mipLevels; texture->viewDimension = viewDesc.dimension; texture->descriptorIndex = g_textureDescriptorAllocator.allocate(); @@ -3221,13 +3221,14 @@ static void FlushViewport() } } -static void StretchRect(GuestDevice* device, uint32_t flags, uint32_t, GuestTexture* texture) +static void StretchRect(GuestDevice* device, uint32_t flags, uint32_t, GuestTexture* texture, uint32_t, uint32_t, uint32_t destSliceOrFace) { // printf("StretchRect %x\n", texture); RenderCommand cmd; cmd.type = RenderCommandType::StretchRect; cmd.stretchRect.flags = flags; cmd.stretchRect.texture = texture; + cmd.stretchRect.destSliceOrFace = destSliceOrFace; g_renderQueue.enqueue(cmd); } @@ -3247,7 +3248,7 @@ static void ProcStretchRect(const RenderCommand& cmd) args.texture->sourceSurface = surface; // printf("ProcStretchRect: surface - %x %x ? (%x : %x)\n", surface, isDepthStencil, g_depthStencil, g_renderTarget); - surface->destinationTextures.emplace(args.texture); + surface->destinationTextures.emplace(args.texture, args.destSliceOrFace); // If the texture is assigned to any slots, set it again. This'll also push the barrier. for (uint32_t i = 0; i < std::size(g_textures); i++) @@ -3373,7 +3374,7 @@ static bool PopulateBarriersForStretchRect(GuestSurface* renderTarget, GuestSurf AddBarrier(surface, srcLayout); - for (const auto texture : surface->destinationTextures) + for (const auto [texture, _] : surface->destinationTextures) AddBarrier(texture, dstLayout); addedAny = true; @@ -3393,7 +3394,7 @@ static void ExecutePendingStretchRectCommands(GuestSurface* renderTarget, GuestS { const bool multiSampling = surface->sampleCount != RenderSampleCount::COUNT_1; - for (const auto texture : surface->destinationTextures) + for (const auto [texture, slice] : surface->destinationTextures) { bool shaderResolve = true; @@ -3483,27 +3484,36 @@ static void ExecutePendingStretchRectCommands(GuestSurface* renderTarget, GuestS } } - if (texture->framebuffer == nullptr) + auto& framebuffer = texture->framebuffers[slice]; + if (framebuffer == nullptr) { if (texture->format == RenderFormat::D32_FLOAT) { + RenderTextureViewDesc viewDesc; + viewDesc.format = texture->format; + viewDesc.dimension = texture->viewDimension; + viewDesc.mipLevels = texture->mipLevels; + viewDesc.arrayIndex = slice; + viewDesc.arraySize = 1; + auto& view = texture->framebufferViews.emplace_back(texture->texture->createTextureView(viewDesc)); + RenderFramebufferDesc desc; - desc.depthAttachment = texture->texture; - texture->framebuffer = g_device->createFramebuffer(desc); + desc.depthAttachmentView = view.get(); + framebuffer = g_device->createFramebuffer(desc); } else { RenderFramebufferDesc desc; desc.colorAttachments = const_cast(&texture->texture); desc.colorAttachmentsCount = 1; - texture->framebuffer = g_device->createFramebuffer(desc); + framebuffer = g_device->createFramebuffer(desc); } } - if (g_framebuffer != texture->framebuffer.get()) + if (g_framebuffer != framebuffer.get()) { - commandList->setFramebuffer(texture->framebuffer.get()); - g_framebuffer = texture->framebuffer.get(); + commandList->setFramebuffer(framebuffer.get()); + g_framebuffer = framebuffer.get(); } commandList->setPipeline(pipeline); @@ -3559,7 +3569,7 @@ static void ProcExecutePendingStretchRectCommands(const RenderCommand& cmd) if (surface->format != RenderFormat::D32_FLOAT) ExecutePendingStretchRectCommands(surface, nullptr); - for (const auto texture : surface->destinationTextures) + for (const auto [texture, _] : surface->destinationTextures) texture->sourceSurface = nullptr; surface->destinationTextures.clear(); @@ -5756,6 +5766,7 @@ static bool LoadTexture(GuestTexture& texture, const uint8_t* data, size_t dataS texture.width = ddsDesc.width; texture.height = ddsDesc.height; + texture.mipLevels = viewDesc.mipLevels; texture.viewDimension = viewDesc.dimension; struct Slice @@ -8068,7 +8079,7 @@ int D3DDevice_EndTiling(GuestDevice* device, uint32_t flags, Rect* pResolveRects // printf("pResolveParams: %x %x %x\n", resolveParams->format.get(), resolveParams->unk.get(), resolveParams->format2.get()); // } if (pDestTexture) { - StretchRect(device, flags, 0, pDestTexture); + StretchRect(device, flags, 0, pDestTexture, 0, 0, 0); } return 0; } diff --git a/MarathonRecomp/gpu/video.h b/MarathonRecomp/gpu/video.h index db15c8cd2..deba130c5 100644 --- a/MarathonRecomp/gpu/video.h +++ b/MarathonRecomp/gpu/video.h @@ -169,9 +169,11 @@ struct GuestBaseTexture : GuestResource struct GuestTexture : GuestBaseTexture { uint32_t depth = 0; + uint32_t mipLevels = 1; RenderTextureViewDimension viewDimension = RenderTextureViewDimension::UNKNOWN; void* mappedMemory = nullptr; - std::unique_ptr framebuffer; + ankerl::unordered_dense::map> framebuffers; + std::vector> framebufferViews; std::unique_ptr patchedTexture; struct GuestSurface* sourceSurface = nullptr; }; @@ -228,7 +230,7 @@ struct GuestSurface : GuestBaseTexture uint32_t guestFormat = 0; ankerl::unordered_dense::map> framebuffers; RenderSampleCounts sampleCount = RenderSampleCount::COUNT_1; - ankerl::unordered_dense::set destinationTextures; + ankerl::unordered_dense::map destinationTextures; bool wasCached = false; }; diff --git a/MarathonRecomp/ui/game_window.cpp b/MarathonRecomp/ui/game_window.cpp index 6e20ad750..90e2d06a6 100644 --- a/MarathonRecomp/ui/game_window.cpp +++ b/MarathonRecomp/ui/game_window.cpp @@ -217,7 +217,7 @@ void GameWindow::Init(const char* sdlVideoDriver) #elif defined(__linux__) s_renderWindow = { info.info.x11.display, info.info.x11.window }; #elif defined(__APPLE__) - s_renderWindow.window = s_pWindow; + s_renderWindow.window = info.info.cocoa.window; s_renderWindow.view = SDL_Metal_GetLayer(SDL_Metal_CreateView(s_pWindow)); #else static_assert(false, "Unknown platform.");