Skip to content

Commit

Permalink
v1.153:
Browse files Browse the repository at this point in the history
HIGHLIGHTS:
- Previously NRI assumed only top-left (D3D style) viewport origin. Now this behavior has been extended to support bottom-left origin too (top-left is still the default). For this purpose "DeviceDesc::isViewportOriginBottomLeftSupported" has been introduced and added "Viewport::originBottomLeft" as the last field in the struct, unlocking maximum flexibility.

BREAKING CHANGES:
- "depthRangeMin" and "depthRangeMax" renamed to "depthMin" and "depthMax" to look closer to the D3D/VK names

DETAILS:
- NRI: added viewport origin bottom-left support (unsupported by D3D11)
- WrapperVK: added ability to enable NRI validation
- VK: fixed viewport Y flipping code
- VK: updated headers to v1.3.296
- polishing
- updated docs
  • Loading branch information
dzhdanNV committed Oct 21, 2024
1 parent 7c1db3a commit d66a97f
Show file tree
Hide file tree
Showing 20 changed files with 122 additions and 69 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[submodule "External/vulkan"]
path = External/vulkan
url = https://github.com/KhronosGroup/Vulkan-Headers.git
branch = origin/vulkan-sdk-1.3.290
branch = origin/vulkan-sdk-1.3.296
update = merge
[submodule "External/nvapi"]
path = External/nvapi
Expand Down
6 changes: 4 additions & 2 deletions Include/Extensions/NRIWrapperD3D11.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ NriStruct(DeviceCreationD3D11Desc) {
NriOptional AGSContext* agsContext;
Nri(CallbackInterface) callbackInterface;
Nri(AllocationCallbacks) allocationCallbacks;
bool enableD3D11CommandBufferEmulation;
bool enableNRIValidation;
bool isNVAPILoaded; // at least NVAPI requires calling "NvAPI_Initialize" in DLL/EXE where the device is created in addition to NRI

// Switches (disabled by default)
bool enableNRIValidation;
bool enableD3D11CommandBufferEmulation; // enable? but why? (auto-enabled if deferred contexts are not supported)
};

NriStruct(CommandBufferD3D11Desc) {
Expand Down
6 changes: 4 additions & 2 deletions Include/Extensions/NRIWrapperD3D12.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ NriStruct(DeviceCreationD3D12Desc) {
NriOptional AGSContext* agsContext;
Nri(CallbackInterface) callbackInterface;
Nri(AllocationCallbacks) allocationCallbacks;
bool enableD3D12DrawParametersEmulation;
bool enableNRIValidation;
bool isNVAPILoaded; // at least NVAPI requires calling "NvAPI_Initialize" in DLL/EXE where the device is created in addition to NRI

// Switches (disabled by default)
bool enableNRIValidation;
bool enableD3D12DrawParametersEmulation;
};

NriStruct(CommandBufferD3D12Desc) {
Expand Down
3 changes: 3 additions & 0 deletions Include/Extensions/NRIWrapperVK.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ NriStruct(DeviceCreationVKDesc) {
uint32_t queueFamilyIndexNum;
const char* libraryPath;
uint8_t minorVersion; // >= 2

// Switches (disabled by default)
bool enableNRIValidation;
};

NriStruct(CommandQueueVKDesc) {
Expand Down
4 changes: 2 additions & 2 deletions Include/NRI.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Non-goals:
#pragma once

#define NRI_VERSION_MAJOR 1
#define NRI_VERSION_MINOR 152
#define NRI_VERSION_DATE "7 October 2024"
#define NRI_VERSION_MINOR 153
#define NRI_VERSION_DATE "21 October 2024"

#include "NRIDescs.h"

Expand Down
2 changes: 1 addition & 1 deletion Include/NRICompatibility.hlsli
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ Draw parameters:
struct name
#endif

// Misc
// Misc (assumes D3D-style viewport)
#define NRI_UV_TO_CLIP(uv) (uv * float2(2, -2) + float2(-1, 1))
#define NRI_CLIP_TO_UV(clip) (clip * float2(0.5, -0.5) + 0.5)

Expand Down
27 changes: 15 additions & 12 deletions Include/NRIDescs.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

#include <stdint.h>

#if defined(__clang__) || defined(__GNUC__)
#define NRI_CALL
#else
#if defined(_WIN32)
#define NRI_CALL __stdcall
#else
#define NRI_CALL
#endif

#ifndef NRI_API
Expand Down Expand Up @@ -291,20 +291,22 @@ NriBits(StageBits, uint32_t,
NriMember(StageBits, COLOR_ATTACHMENT)
);

NriStruct(Rect) {
int16_t x;
int16_t y;
Nri(Dim_t) width;
Nri(Dim_t) height;
};

// The viewport origin is top-left (D3D native) by default, but can be changed to bottom-left (VK native)
NriStruct(Viewport) {
float x;
float y;
float width;
float height;
float depthRangeMin;
float depthRangeMax;
float depthMin;
float depthMax;
bool originBottomLeft; // expects "isViewportOriginBottomLeftSupported"
};

NriStruct(Rect) {
int16_t x;
int16_t y;
Nri(Dim_t) width;
Nri(Dim_t) height;
};

NriStruct(Color32f) {
Expand Down Expand Up @@ -1471,6 +1473,7 @@ NriStruct(DeviceDesc) {
uint32_t isMemoryTier2Supported : 1; // a memory object can support resources from all 3 categories (buffers, attachments, all other textures)
uint32_t isDynamicDepthBiasSupported : 1;
uint32_t isAdditionalShadingRatesSupported : 1;
uint32_t isViewportOriginBottomLeftSupported : 1;

// Shader features
uint32_t isShaderNativeI16Supported : 1;
Expand Down
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

[![Status](https://github.com/NVIDIAGameWorks/NRI/actions/workflows/build.yml/badge.svg)](https://github.com/NVIDIAGameWorks/NRI/actions/workflows/build.yml)

*NRI* is a low-level abstract render interface which currently supports three backends: D3D11, D3D12 and Vulkan (VK). *NRI* has been designed to support all (at least major) low level features of D3D12 and VK APIs, but at the same time to simplify usage and reduce the amount of code needed (especially compared with VK). *NRI* is written in *C++*, but supports both *C++* and *C* interfaces.
*NRI* is a low-level abstract render interface, which has been designed to support all low level features of D3D12 and Vulkan GAPIs, but at the same time to simplify usage and reduce the amount of code needed (especially compared with VK).

Goals:
- generalization of D3D12 ([spec](https://microsoft.github.io/DirectX-Specs/)) and VK ([spec](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html)) GAPIs
- providing access to low-level features of modern GAPIs
- providing high-level "quality of life" improving utilities, organized as extensions
- low overhead
- explicitness
- D3D11 ([spec](https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm)) support (as much as possible)
Expand All @@ -17,6 +18,12 @@ Non-goals:
- D3D11-like abstraction level
- hidden management of any kind

Currently supported GAPIs:
- Vulkan (VK)
- D3D12
- D3D11
- None / dummy (everything is supported, but does nothing)

Key features:
- *C++* and *C* compatible interfaces
- generalized common denominator for D3D12, VK and D3D11 GAPIs
Expand All @@ -43,9 +50,9 @@ Key features:

*(some interfaces can be missing in the listing)*

*NRI* is used in:
*NRI* sample code:
- [*NRI samples*](https://github.com/NVIDIAGameWorks/NRISamples)
- [*NRD Sample*](https://github.com/NVIDIAGameWorks/NRDSample)
- [*NRD sample*](https://github.com/NVIDIAGameWorks/NRDSample)

## C/C++ INTERFACE DIFFERENCES

Expand Down Expand Up @@ -146,7 +153,7 @@ Samples:
- AsyncCompute - demonstrates parallel execution of graphic and compute workloads
- MultiThreading - shows advantages of multi-threaded command buffer recording
- MultiGPU - multi GPU example
- RayTracingTriangle - simple triangle rendeing through ray tracing
- RayTracingTriangle - simple triangle rendering through ray tracing
- RayTracingBoxes - a more advanced ray tracing example with many BLASes in TLAS
- Wrapper - shows how to wrap native D3D11/D3D12/VK objects into NRI entities
- Resize - demonstrates window resize
Expand Down
2 changes: 1 addition & 1 deletion Resources/Version.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#define STR(x) STR_HELPER(x)

#define VERSION_MAJOR 1
#define VERSION_MINOR 152
#define VERSION_MINOR 153
#define VERSION_BUILD 0
#define VERSION_REVISION 0

Expand Down
2 changes: 1 addition & 1 deletion Source/Creation/Creation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ NRI_API Result NRI_CALL nriCreateDeviceFromVkDevice(const DeviceCreationVKDesc&
deviceCreationDesc.allocationCallbacks = deviceCreationVKDesc.allocationCallbacks;
deviceCreationDesc.spirvBindingOffsets = deviceCreationVKDesc.spirvBindingOffsets;
deviceCreationDesc.graphicsAPI = GraphicsAPI::VK;
deviceCreationDesc.enableNRIValidation = false;
deviceCreationDesc.enableNRIValidation = deviceCreationVKDesc.enableNRIValidation;

CheckAndSetDefaultCallbacks(deviceCreationDesc.callbackInterface);
CheckAndSetDefaultAllocator(deviceCreationDesc.allocationCallbacks);
Expand Down
14 changes: 13 additions & 1 deletion Source/D3D11/CommandBufferD3D11.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,19 @@ NRI_INLINE Result CommandBufferD3D11::End() {
}

NRI_INLINE void CommandBufferD3D11::SetViewports(const Viewport* viewports, uint32_t viewportNum) {
m_DeferredContext->RSSetViewports(viewportNum, (const D3D11_VIEWPORT*)viewports);
Scratch<D3D11_VIEWPORT> d3dViewports = AllocateScratch(m_Device, D3D11_VIEWPORT, viewportNum);
for (uint32_t i = 0; i < viewportNum; i++) {
const Viewport& in = viewports[i];
D3D11_VIEWPORT& out = d3dViewports[i];
out.TopLeftX = in.x;
out.TopLeftY = in.y;
out.Width = in.width;
out.Height = in.height;
out.MinDepth = in.depthMin;
out.MaxDepth = in.depthMax;
}

m_DeferredContext->RSSetViewports(viewportNum, d3dViewports);
}

NRI_INLINE void CommandBufferD3D11::SetScissors(const Rect* rects, uint32_t rectNum) {
Expand Down
48 changes: 35 additions & 13 deletions Source/D3D12/CommandBufferD3D12.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,15 @@ static void AddResourceBarrier(D3D12_COMMAND_LIST_TYPE commandListType, ID3D12Re
}
}

static inline void ConvertRects(const Rect* in, uint32_t rectNum, D3D12_RECT* out) {
for (uint32_t i = 0; i < rectNum; i++) {
out[i].left = in[i].x;
out[i].top = in[i].y;
out[i].right = in[i].x + in[i].width;
out[i].bottom = in[i].y + in[i].height;
}
}

Result CommandBufferD3D12::Create(D3D12_COMMAND_LIST_TYPE commandListType, ID3D12CommandAllocator* commandAllocator) {
ComPtr<ID3D12GraphicsCommandListBest> graphicsCommandList;
HRESULT hr = m_Device->CreateCommandList(NRI_NODE_MASK, commandListType, commandAllocator, nullptr, __uuidof(ID3D12GraphicsCommandList), (void**)&graphicsCommandList);
Expand Down Expand Up @@ -261,19 +270,32 @@ NRI_INLINE Result CommandBufferD3D12::End() {
}

NRI_INLINE void CommandBufferD3D12::SetViewports(const Viewport* viewports, uint32_t viewportNum) {
static_assert(offsetof(Viewport, x) == 0, "Unsupported viewport data layout");
static_assert(offsetof(Viewport, width) == 8, "Unsupported viewport data layout");
static_assert(offsetof(Viewport, depthRangeMin) == 16, "Unsupported viewport data layout");
static_assert(offsetof(Viewport, depthRangeMax) == 20, "Unsupported viewport data layout");
Scratch<D3D12_VIEWPORT> d3dViewports = AllocateScratch(m_Device, D3D12_VIEWPORT, viewportNum);
for (uint32_t i = 0; i < viewportNum; i++) {
const Viewport& in = viewports[i];
D3D12_VIEWPORT& out = d3dViewports[i];
out.TopLeftX = in.x;
out.TopLeftY = in.y;
out.Width = in.width;
out.Height = in.height;
out.MinDepth = in.depthMin;
out.MaxDepth = in.depthMax;

// Origin bottom-left requires flipping
if (in.originBottomLeft) {
out.TopLeftY += in.height;
out.Height = -in.height;
}
}

m_GraphicsCommandList->RSSetViewports(viewportNum, (D3D12_VIEWPORT*)viewports);
m_GraphicsCommandList->RSSetViewports(viewportNum, d3dViewports);
}

NRI_INLINE void CommandBufferD3D12::SetScissors(const Rect* rects, uint32_t rectNum) {
Scratch<D3D12_RECT> rectsD3D12 = AllocateScratch(m_Device, D3D12_RECT, rectNum);
ConvertRects(rectsD3D12, rects, rectNum);
Scratch<D3D12_RECT> d3dRects = AllocateScratch(m_Device, D3D12_RECT, rectNum);
ConvertRects(rects, rectNum, d3dRects);

m_GraphicsCommandList->RSSetScissorRects(rectNum, rectsD3D12);
m_GraphicsCommandList->RSSetScissorRects(rectNum, d3dRects);
}

NRI_INLINE void CommandBufferD3D12::SetDepthBounds(float boundsMin, float boundsMax) {
Expand Down Expand Up @@ -324,20 +346,20 @@ NRI_INLINE void CommandBufferD3D12::ClearAttachments(const ClearDesc* clearDescs
if (!clearDescNum)
return;

Scratch<D3D12_RECT> rectsD3D12 = AllocateScratch(m_Device, D3D12_RECT, rectNum);
ConvertRects(rectsD3D12, rects, rectNum);
Scratch<D3D12_RECT> d3dRects = AllocateScratch(m_Device, D3D12_RECT, rectNum);
ConvertRects(rects, rectNum, d3dRects);

for (uint32_t i = 0; i < clearDescNum; i++) {
if (clearDescs[i].planes & PlaneBits::COLOR)
m_GraphicsCommandList->ClearRenderTargetView(m_RenderTargets[clearDescs[i].colorAttachmentIndex], &clearDescs[i].value.color.f.x, rectNum, rectsD3D12);
m_GraphicsCommandList->ClearRenderTargetView(m_RenderTargets[clearDescs[i].colorAttachmentIndex], &clearDescs[i].value.color.f.x, rectNum, d3dRects);
else {
D3D12_CLEAR_FLAGS clearFlags = (D3D12_CLEAR_FLAGS)0;
if (clearDescs[i].planes & PlaneBits::DEPTH)
clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
if (clearDescs[i].planes & PlaneBits::STENCIL)
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;

m_GraphicsCommandList->ClearDepthStencilView(m_DepthStencil, clearFlags, clearDescs[i].value.depthStencil.depth, clearDescs[i].value.depthStencil.stencil, rectNum, rectsD3D12);
m_GraphicsCommandList->ClearDepthStencilView(m_DepthStencil, clearFlags, clearDescs[i].value.depthStencil.depth, clearDescs[i].value.depthStencil.stencil, rectNum, d3dRects);
}
}
}
Expand Down Expand Up @@ -563,7 +585,7 @@ NRI_INLINE void CommandBufferD3D12::CopyTexture(Texture& dstTexture, const Textu
srcRegion->height == WHOLE_SIZE ? srcTextureD3D12.GetSize(1, srcRegion->mipOffset) : srcRegion->height,
srcRegion->depth == WHOLE_SIZE ? srcTextureD3D12.GetSize(2, srcRegion->mipOffset) : srcRegion->depth,
};

D3D12_BOX box = {
srcRegion->x,
srcRegion->y,
Expand Down
1 change: 1 addition & 0 deletions Source/D3D12/DeviceD3D12.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ void DeviceD3D12::FillDesc(const DeviceCreationDesc& deviceCreationDesc) {
REPORT_WARNING(this, "ID3D12Device::CheckFeatureSupport(options13) failed, result = 0x%08X!", hr);
m_Desc.uploadBufferTextureRowAlignment = options13.UnrestrictedBufferTextureCopyPitchSupported ? 1 : D3D12_TEXTURE_DATA_PITCH_ALIGNMENT;
m_Desc.uploadBufferTextureSliceAlignment = options13.UnrestrictedBufferTextureCopyPitchSupported ? 1 : D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT;
m_Desc.isViewportOriginBottomLeftSupported = options13.InvertedViewportHeightFlipsYSupported ? 1 : 0;

// Minimum supported client: Agility SDK
D3D12_FEATURE_DATA_D3D12_OPTIONS14 options14 = {};
Expand Down
1 change: 0 additions & 1 deletion Source/D3D12/SharedD3D12.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ struct DescriptorHeapDesc {
void GetResourceDesc(D3D12_RESOURCE_DESC* desc, const BufferDesc& bufferDesc);
void GetResourceDesc(D3D12_RESOURCE_DESC* desc, const TextureDesc& textureDesc);
void ConvertGeometryDescs(D3D12_RAYTRACING_GEOMETRY_DESC* geometryDescs, const GeometryObject* geometryObjects, uint32_t geometryObjectNum);
void ConvertRects(D3D12_RECT* rectsD3D12, const Rect* rects, uint32_t rectNum);
bool GetTextureDesc(const TextureD3D12Desc& textureD3D12Desc, TextureDesc& textureDesc);
bool GetBufferDesc(const BufferD3D12Desc& bufferD3D12Desc, BufferDesc& bufferDesc);
uint64_t GetMemorySizeD3D12(const MemoryD3D12Desc& memoryD3D12Desc);
Expand Down
9 changes: 0 additions & 9 deletions Source/D3D12/SharedD3D12.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,15 +395,6 @@ void nri::ConvertGeometryDescs(D3D12_RAYTRACING_GEOMETRY_DESC* geometryDescs, co
}
}

void nri::ConvertRects(D3D12_RECT* rectsD3D12, const Rect* rects, uint32_t rectNum) {
for (uint32_t i = 0; i < rectNum; i++) {
rectsD3D12[i].left = rects[i].x;
rectsD3D12[i].top = rects[i].y;
rectsD3D12[i].right = rects[i].x + rects[i].width;
rectsD3D12[i].bottom = rects[i].y + rects[i].height;
}
}

uint64_t nri::GetMemorySizeD3D12(const MemoryD3D12Desc& memoryD3D12Desc) {
return memoryD3D12Desc.d3d12Heap->GetDesc().SizeInBytes;
}
Expand Down
1 change: 1 addition & 0 deletions Source/NONE/ImplNONE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ struct DeviceNONE final : public DeviceBase {
m_Desc.isMemoryTier2Supported = true;
m_Desc.isDynamicDepthBiasSupported = true;
m_Desc.isAdditionalShadingRatesSupported = true;
m_Desc.isViewportOriginBottomLeftSupported = true;

m_Desc.isShaderNativeI16Supported = true;
m_Desc.isShaderNativeF16Supported = true;
Expand Down
38 changes: 20 additions & 18 deletions Source/VK/CommandBufferVK.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,20 @@ NRI_INLINE Result CommandBufferVK::End() {
NRI_INLINE void CommandBufferVK::SetViewports(const Viewport* viewports, uint32_t viewportNum) {
Scratch<VkViewport> vkViewports = AllocateScratch(m_Device, VkViewport, viewportNum);
for (uint32_t i = 0; i < viewportNum; i++) {
const Viewport& viewport = viewports[i];
VkViewport& vkViewport = vkViewports[i];
vkViewport.x = viewport.x;
vkViewport.y = viewport.y;
vkViewport.width = viewport.width;
vkViewport.height = viewport.height;
vkViewport.minDepth = viewport.depthRangeMin;
vkViewport.maxDepth = viewport.depthRangeMax;

// Flip
vkViewport.y = viewport.height - viewport.y;
vkViewport.height = -viewport.height;
const Viewport& in = viewports[i];
VkViewport& out = vkViewports[i];
out.x = in.x;
out.y = in.y;
out.width = in.width;
out.height = in.height;
out.minDepth = in.depthMin;
out.maxDepth = in.depthMax;

// Origin top-left requires flipping
if (!in.originBottomLeft) {
out.y += in.height;
out.height = -in.height;
}
}

const auto& vk = m_Device.GetDispatchTable();
Expand All @@ -75,12 +77,12 @@ NRI_INLINE void CommandBufferVK::SetViewports(const Viewport* viewports, uint32_
NRI_INLINE void CommandBufferVK::SetScissors(const Rect* rects, uint32_t rectNum) {
Scratch<VkRect2D> vkRects = AllocateScratch(m_Device, VkRect2D, rectNum);
for (uint32_t i = 0; i < rectNum; i++) {
const Rect& viewport = rects[i];
VkRect2D& vkRect = vkRects[i];
vkRect.offset.x = viewport.x;
vkRect.offset.y = viewport.y;
vkRect.extent.width = viewport.width;
vkRect.extent.height = viewport.height;
const Rect& in = rects[i];
VkRect2D& out = vkRects[i];
out.offset.x = in.x;
out.offset.y = in.y;
out.extent.width = in.width;
out.extent.height = in.height;
}

const auto& vk = m_Device.GetDispatchTable();
Expand Down
Loading

0 comments on commit d66a97f

Please sign in to comment.