Storing SH coefficients. Texture vs Buffer

Very large scenes may require 1000s of SH probes which means a lot of coefficients that need to be stored and read from a pixel shader.

I'm wondering if there is a consensus on the most efficient way to store the coefficients themselves: uncompressed floats in some large array buffer, or as compressed pixels in a texture.

For an order 3 SH we need to store 16 float3 coefficients, which in my mind maps perfectly to a single BC block, so by ordering the 4x4 pixel blocks spatially in a BC6h SF16 texture we get an efficiency of 1 byte per float3 coefficient vs the 4 bytes per coeff for a buffer.

However when using a texture we have to go through the texture sampling hardware. So the question is if it's worth it: does the lower memory bandwidth make up for an additional 16 texture lookups per pixel per sampled probe, or would we get better performance just indexing into a structured buffer?

reddit.com
u/Avelina9X — 2 days ago

Correct Way to Recreate D3D11_MAP_WRITE_DISCARD in D3D12?

I'm trying to emulate the functionality of D3D11_MAP_WRITE_DISCARD in D3D12 for a bit of an interesting use case.

As I understand, small resources that change every frame like CBVs can sit in an UPLOAD heap, and should be N-buffered for N frames in flight. And larger static resources should be copied into the DEFAULT heap to stay resident in GPU VRAM.

However what is the correct pattern for larger resources that change frequently, sometimes every frame but sometimes rarely?

Right now I see two options.

The first option would be to have N copies in the DEFAULT heap and N re-usable buffers in the UPLOAD heap. The index of the upload buffer in use corresponds to the current frame index modulo N, however the actual resource we use for draw calls is not coupled to the frame index, and is instead chosen based on whichever DEFAULT buffer we completed writing to last. We use fence values to track when the queue executing the UPLOAD->DEFAULT copy is completed so we can update the "most recent" index to point to the correct buffer.

If the buffer is something that can change in size, like a vertex buffer, we reallocate the buffers with some growth factor if needed and we dump the stale DEFAULT and UPLOAD resources into a list with their corresponding fence values to be freed later once safe to do so.

The second option involves something like the D3D12MemoryAllocator. We still have N copies in the DEFAULT heap (which will be growth reallocated and freed in a similar manner to option 1 if needed), but for the UPLOAD buffers we create, reuse, reallocate, and free as necessary inside a D3D12MA UPLOAD heap, almost as if it were a ring buffer (but not necessarily a true ring buffer since the pages may be shared with other resources allocated using the D3D12MA).

Then for either option we just stick references to the resources and their fence values in a little struct, slap on a "UpdateData()" method which handles all the resource jugging, and maybe a "BeginFrame()" method which takes the current frame's fence value and a fence pointer so we can check if copies are completed or anything stale needs to be freed... and I think that's it?

Am I missing anything?

reddit.com
u/Avelina9X — 11 days ago

A fix for your SM 6.6+ GPU showing up as SM 6.5 in DirectX 12

I'm writing this in hopes of it getting picked up by search engines because this fix is nowhere to be found from google!

The Problem

So... you have a GPU capable of SM6.6+, your version of Windows supports SM6.6+, you're using a recent driver which supports SM6.6+... but when you run

mDevice->CheckFeatureSupport( D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof( shaderModel ) );

the value of shaderModel.HighestShaderModel returns a value of 101, aka 0x65, which is D3D_SHADER_MODEL_6_5.

Why?

The version of the d3d12.dll that ships with windows does not know Shader Models above 6.5 even exist. If you use dxcapsviewer.exe it will also not show any version greater than 6.5.

How To Fix

The fix is incredibly simple. The AgilitySDK ships with a DLL called D3D12Core.dll which should be (automatically if used with NuGet) placed in a folder called /D3D12/ next to your executable.

This DLL is what is needed to query support higher than SM 6.5 using the CheckFeatureSupport method, BUT this DLL does NOT get loaded automatically. The D3DX12/AgilitySDK library will only load this DLL if you specifically define the following constants.

extern "C"
{
  __declspec(dllexport) extern const UINT D3D12SDKVersion = D3D12_SDK_VERSION;
  __declspec(dllexport) extern const char* D3D12SDKPath = reinterpret_cast<const char*>(u8".\\D3D12\\");
}

That's literally it. The AgilitySDK through NuGet creates a custom property page that defines the D3D12SDKPath itself but this is only used for a build step that copies the DLLs over, it doesn't use any fancy bullshit to actually inform the executable of this location. You gotta do that manually.

And note that D3D12_SDK_VERSION may or may not be defined, nor have the right value depending on which version of d3d12.h is actually included first. D3DX12 comes with it's own version of the header that correctly #defines the SDK version, but you may want to specify the value manually (619 as of writing) if you still don't get the right Shader Model returned from the query.

(Also the reinterpret_cast is only required for C++20 to stop it from complaining that char8 might not be the same width as char. I don't even know if we need to specify the literal as u8, but the current state of unicode/multibyte/whatever support in MSVC scares me and is above my pay grade.)

reddit.com
u/Avelina9X — 13 days ago

Any reason not to go Bindless in DX12?

Since moving from DX11 to DX12 binding has become much more explicit. We have to manage descriptor heaps/tables and effectively do all the resource management ourselves. Bindless seems to alleviate some of these issues by simply binding a reserved range with unbounded descriptor indices in the shader, and then use either a CBV or root constants to index into those arrays.

This seems significantly more convenient, but are there any cases where this would result in worse performance or have any other drawbacks?

As far as I can tell bindless textures or other "concrete" typed buffers have no drawback, but when doing bindless vertex data or heterogeneous buffers we have to fully manage the type-conversion ourselves. But other than that... are there any real drawbacks or performance concerns outside of all the articles I read that say "In my experience the performance is the same on XYZ graphics card"

reddit.com
u/Avelina9X — 13 days ago

Debating Building My Own Gizmo Library

I've been debating building my own Gizmo library for a while now after using both ImGuizmo and Im3D and finding them both inadequate. I've got a list of issues I have with existing libraries which is my main motivation for building my own. So if any of you guys know of a lib that solves these problems I'd love to hear about it, otherwise I'm probably going to build my own:

1. The Horizon

Most Gizmo libs (as well as gizmos included in software such as Blender) go completely haywire after the mouse is moved beyond the horizon for translations. Some of them start moving backwards from behind the camera, and some of them (like ImGuizmo) rapidly bounce back and forth between the near and far clipping planes.

I managed to solve this issue with ImGuizmo by only using the actual Gizmo as a proxy for detecting if a handle is held, and then writing my own projection code using gnomonic projection which creates a "flipped" plane above the horizon. Problem is this is all additional proxy code on top of the actual Gizmo itself, and requires a bunch of extra stateful bullshit because most of the data we need to calculate this is private within the gizmo libs context.

2. Immediate Mode

Why for the love of fuck are all the Gizmo libraries Immediate mode? It makes them easy to use but painful for anything even remotely complex because of the global state.

3. Customisation

Im3D is honestly quite nice, but it has some major drawbacks in that we only have translation/rotation/scale gizmos to play with and they are always 3 axis. No single axis translation which we could repurpose for stuff like extrusion handles or whatever.

And ImGuizmo, while more customisable through disabling axes, isn't much better because - again - every point of customisation is locked behind the internal API. There are also problems with using Gizmos with ImGui when the mouse owned by another ImGui ID... so if we're using a canvas of sorts as a render target we can't using ImGuizmo Gizmos because clicking on the canvas takes mouse ownership and disables the Gizmo; I had to rip out the ownership code itself to invert this functionality, because I want the Gizmo to be selectable only when the canvas owns the mouse, not the other way round.

4. The Math

We're forced to use whatever math is provided by the lib itself, which is usually a bunch of hand rolled floats. No SSE or AVX2 intrinsics, and we're forced to use single precision floats which makes manipulating double precision transforms a pain because now we have to rebase everything when far from the origin.

Additionally all these libraries seem to use Pitch Yaw Roll for rotations internally which becomes a gimbal lock fucking nightmare. I've only been able to solve this by getting the Gizmo to spit out a transformation matrix, decompose into TRS, convert R into quaternion, use the quaternion to compute a delta against the original matrix decomposition, then do a matrix quat multiplication against the delta to get the new rotation. If we don't do that the local space rotations get absolutely fucked, and ImGuizmo currently has an open issue for this exact problem.

There are probably more issues, but these are the main ones. If anyone knows of a library that solves these problems I'd love to hear it... otherwise I'm probably going to roll my own using ImGui for an input API and DX12 for rendering.

reddit.com
u/Avelina9X — 22 days ago

Built my own std::vector replacement. Am I missing anything?

So I have a weird use case where the size of a std::vector actually matters, and 24 bytes becomes a little wasteful in terms of memory layout when a vector is a member of another struct and we have an array of such structs.

So I built my own std::vector replacement which uses uint32_t size and capacity members, rather than 8 byte last and end pointers.

https://github.com/EntropyEngine/LibEntropy/blob/main/src/LibEntropy/Container/Array.hpp

Otherwise, both should perform almost identically with two differences:
- there is no aliasing support, so we cannot insert/push/emplace a reference to an element inside the array.
- there is no rollback or try/catch if a throw occurs during move/copy when reallocating or mutating.

I also have yet to correctly implement initialization when calling resize(), right now it leaves trivially default constructible types with uninitialised memory because I'm debating if I should add some sort of control over zero init vs non init for such types using maybe some custom type trait or a seperate method.
Update: committed to zero init as standard behaviour. A resize_uninitialized method is made available for trivial types, may be useful for HVAs etc.

But other than that, I wanna make sure I'm not missing anything. My unit tests are all passing, but that doesn't mean I haven't done something in a stupid or inefficient way, missed an edge case or am missing coverage.

I know this isn't really a "beginner" question, but I'd prefer feedback from humans over feedback from an AI, so this is massively appreciated!

(Oh and by the way this code is not AI slop, I just write very rebose comments to help myself figure things out.)

u/Avelina9X — 1 month ago
▲ 0 r/Twitch

https://preview.redd.it/bxbu0ju1wkyg1.png?width=430&format=png&auto=webp&s=18efda7b933e2ef5d0c7c1354f992de67d4a4296

Yesterday pretty much all UK/EU servers (and apparently one NA server?) were failing to relay streams back to users.

This affected several streamers in UK yesterday including myself and my wife, including partnered streamers, and there were reports of the same occuring in other EU regions like Germany.

We decided to end stream, but a partnered streamer we know tried to solve this by switching to other Twitch servers yet the issue persisted on ALL of them.

However all our VODs were saving, despite the stream not getting relayed back to users. The partnered streamer in question ended up with like 6 different 5 minute VODs getting saved as she stopped stream, switched server, then started again over and over trying to get the stream working.

During this the live view counts showed up as either zero or one, despite a confirmed 100-200 users in chat from across the globe, with everyone confirming a #2000. This affected all quality settings too, so the "source" stream and all lower resolution transcodes failed to reach users.

Obviously we don't know exactly what went wrong, but considering:

  1. No user anywhere in the world could watch one of the affected streams,
  2. Many of these users tested watching other (US based) streamers and confirmed their streams were working,
  3. VODs were saving, going live notifs were going out, and twitch showed the channels as live, and
  4. We're all able to stream without issues again, despite neither changing any encoder settings, nor updating stream keys or pretty much anything else in our configs...

...we can confirm this could not be a browser/extension issue, nor an ISP issue, and not some Twitch auth/login/API issue either.

The only (*probably) thing that could cause this would be an outage with the Twitch transcoding/relay servers, specifically on the Server->Viewer side of things, while the Streamer->Server side remained 100% functional.

And Twitch is claiming zero issues? You're telling me no one reported this? Hell, even if no one reported this, you're telling me twitch didn't notice basically ZERO CCV across all streamers in an entire region for over an hour???

reddit.com
u/Avelina9X — 2 months ago