Skip to content

Commit 50d8fa0

Browse files
committed
sync on 2026/06/20, rev b56bb083a422680afc47c84cabba44a7af5b9721
1 parent 5cfeff7 commit 50d8fa0

1,907 files changed

Lines changed: 136304 additions & 44733 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DagorEngine.rev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
12ee3f21d4555dd6a142986ef3d38645f1c0a33e
1+
b56bb083a422680afc47c84cabba44a7af5b9721

_docs/source/api-references/dagor-dshl/index/shaders.rst

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,3 +538,195 @@ Syntax for using such blocks in shaders is as follows:
538538
}
539539
540540
With the support of multiple blocks you can use only variables from intersection of these blocks.
541+
542+
.. _refined-blocks:
543+
544+
==============
545+
Refined Blocks
546+
==============
547+
548+
Refined blocks are an evolution of shader blocks that guarantee **identical cbuffer layout**
549+
across all shaders declaring the same block. This makes it safe to pre-fill a single cbuffer
550+
once and bind it to multiple shaders without per-shader patching.
551+
552+
With traditional shader blocks, the compiler may assign different cbuffer offsets for the same
553+
variable in different shaders (due to varying sets of variables used per shader). Refined blocks
554+
solve this by merging all partial layouts at link time into a single canonical layout shared by
555+
every shader that participates in the block.
556+
557+
----------
558+
Motivation
559+
----------
560+
561+
In a typical rendering frame, many shaders share common parameters (view matrices, lighting data,
562+
etc.). With regular blocks, even though the *values* are the same, the *cbuffer layout* can differ
563+
per shader, requiring a flush-and-refill on each shader switch. Refined blocks eliminate this
564+
overhead: you fill the cbuffer once for all shaders that declare the same refined block.
565+
566+
------
567+
Syntax
568+
------
569+
570+
To include a preshader variable in a refined block, append the ``#rb`` tag to its type suffix:
571+
572+
.. code-block:: text
573+
574+
shader my_shader
575+
{
576+
(cs)
577+
{
578+
my_float@f1#rb = some_global_float;
579+
my_vector@f4#rb = some_global_float4;
580+
my_int@i1#rb = some_global_int;
581+
my_matrix@f44#rb = some_global_matrix;
582+
my_tex@tex2d#rb = some_texture;
583+
my_sampler@sampler#rb = some_sampler;
584+
my_buf@buf#rb = some_buffer hlsl { StructuredBuffer<float> my_buf@buf; };
585+
my_uav@uav#rb = some_uav hlsl { RWStructuredBuffer<float> my_uav@uav; };
586+
my_cbuf@cbuf#rb = some_cbuf hlsl {
587+
cbuffer my_cbuf@cbuf { float4 cbuf_data; };
588+
};
589+
}
590+
}
591+
592+
Variables **without** ``#rb`` in the same preshader block are treated normally and are not part of the refined block.
593+
594+
-------------------
595+
Supported var types
596+
-------------------
597+
598+
The following type suffixes are supported with ``#rb``:
599+
600+
- Numeric: ``@f1``, ``@f2``, ``@f3``, ``@f4``, ``@f44``, ``@i1``, ``@i2``, ``@i3``, ``@i4``, ``@u1``, ``@u2``, ``@u3``, ``@u4``
601+
- Textures: ``@tex2d``, ``@tex3d``, ``@texArray``, ``@texCube``, ``@texCubeArray``
602+
- Samplers: ``@sampler``
603+
- Buffers: ``@buf``, ``@uav``, ``@cbuf``
604+
605+
----------------------
606+
Computed expressions
607+
----------------------
608+
609+
Numeric refined block vars can use arbitrary arithmetic expressions on the right-hand side,
610+
not just simple variable references:
611+
612+
.. code-block:: text
613+
614+
(cs)
615+
{
616+
computed_val@f1#rb = (some_float4.w * another_float4.z + yet_another.y - single_float);
617+
computed_vec@f2#rb = (vec_a.x, vec_b.y);
618+
computed_scaled@f4#rb = (some_float4 * 2);
619+
}
620+
621+
The compiler generates stcode for these expressions that is executed at flush time.
622+
623+
--------------------------
624+
How refined blocks work
625+
--------------------------
626+
627+
1. **Compile time**: The shader compiler collects all ``#rb``-tagged variables across all shaders.
628+
For each unique variable name, an offset in the shared cbuffer is assigned. The merged layout is
629+
stored in the shader dump.
630+
631+
2. **Incremental builds**: Each ``.obj`` caches its partial refined block layout. Unchanged files
632+
skip recomputation. In multiproc mode, the preshader pass merges all partial layouts into a
633+
single ``rblock.layout.obj``; worker processes load this merged layout.
634+
635+
3. **Runtime flush**: The application creates a block hierarchy, sets variable values, and calls
636+
``flush()``. This executes the compiled stcode to pack all variable values into the cbuffer at
637+
their offsets. The resulting cbuffer can then be bound to any shader that uses refined block.
638+
639+
4. **Register allocation**: Textures and buffers with explicit HLSL declarations (``@buf``, ``@uav``,
640+
``@cbuf``) get per-shader register allocations that are reserved so they don't collide with other
641+
bindings. Textures without explicit HLSL type (``@tex2d``, etc.) use bindless resource IDs stored
642+
in the cbuffer when bindless is enabled.
643+
644+
-------------------
645+
Runtime C++ API
646+
-------------------
647+
648+
The runtime API is declared in ``<shaders/dag_refinedBlock.h>``.
649+
650+
**Block hierarchy**
651+
652+
Refined blocks follow a three-layer hierarchy: ``GLOBAL`` → ``VIEW`` → ``PASS``.
653+
Child blocks inherit parent variable values and can shadow them locally.
654+
655+
.. code-block:: cpp
656+
657+
#include <shaders/dag_refinedBlock.h>
658+
659+
auto globalBlock = refined_block::get_global();
660+
auto viewBlock = globalBlock.refineBlock("my_view");
661+
auto passBlock = viewBlock.refineBlock("my_pass");
662+
663+
**Setting variables**
664+
665+
Use ``VariableMap::getVariableId()`` to get variable IDs, then call ``set()`` on any layer:
666+
667+
.. code-block:: cpp
668+
669+
int varF4 = VariableMap::getVariableId("my_float4_var");
670+
globalBlock.set(varF4, Color4(1, 2, 3, 4));
671+
672+
int varTex = VariableMap::getVariableId("my_texture");
673+
passBlock.set(varTex, myTexture.getBaseTex());
674+
675+
int varSampler = VariableMap::getVariableId("my_sampler");
676+
passBlock.set(varSampler, samplerHandle);
677+
678+
int varBuf = VariableMap::getVariableId("my_buffer");
679+
passBlock.set(varBuf, myBuffer.getBuf());
680+
681+
int varUav = VariableMap::getVariableId("my_uav");
682+
passBlock.set(varUav, myUavBuffer.getBuf());
683+
684+
int varCbuf = VariableMap::getVariableId("my_cbuf");
685+
passBlock.set(varCbuf, myCbufBuffer.getBuf());
686+
687+
**Flushing and binding**
688+
689+
Call ``refined_block::flush()`` to pack all blocks into their cbuffers, then ``setState()``
690+
on the pass block to bind resources before dispatch/draw:
691+
692+
.. code-block:: cpp
693+
694+
refined_block::flush();
695+
passBlock.setState();
696+
697+
myShader.dispatch(1, 1, 1);
698+
699+
Call ``refined_block::clear()`` to release all block data (e.g. on shutdown or full reset):
700+
701+
.. code-block:: cpp
702+
703+
refined_block::clear();
704+
705+
------------------------------------
706+
Sharing blocks across shaders
707+
------------------------------------
708+
709+
The key benefit of refined blocks is that multiple shaders declaring the same ``#rb`` variables
710+
share the same cbuffer layout. For example, if ``shader_a`` and ``shader_b`` both declare
711+
``my_vec@f4#rb = some_var;``, the variable will be at the same cbuffer offset in both shaders.
712+
After a single ``flush()``, the same cbuffer can drive both shaders without rebinding.
713+
714+
Variables unique to a single shader (e.g., ``shader1_only@f1#rb``) are still part of the
715+
merged layout but simply unused by shaders that don't declare them.
716+
717+
----------------------------
718+
Mixing refined and regular vars
719+
----------------------------
720+
721+
You can freely mix ``#rb``-tagged and regular preshader vars in the same block:
722+
723+
.. code-block:: text
724+
725+
(cs)
726+
{
727+
shared_data@f4#rb = common_float4; // refined block var
728+
local_data@f4 = shader_specific_float4; // regular per-shader var
729+
}
730+
731+
Regular vars behave as before (per-shader cbuffer layout). Refined block vars share
732+
the layout across all participating shaders.

_docs/source/assets/lighting/photometric_lights.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ These are converted into textures using one of two mapping methods:
355355

356356
To enable spherical mapping:
357357

358-
1. In `<engine_root>/prog/gameLibs/publicInclude/render/renderLights.hlsl`,
358+
1. In `<engine_root>/prog/gameLibs/publicInclude/render/lights/renderLights.hlsl`,
359359
comment or uncomment the line:
360360

361361
```text

outerSpace/prog/scripts/project.das_project

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ let
2424
}}
2525

2626
[export]
27-
def module_get(req, from:string) : tuple<string;string;string> const
27+
def module_get(req, _from:string) : tuple<string;string;string> const
2828
var rs <- split_by_chars(req,"./")
2929

3030
let mod_name = rs[length(rs)-1]
@@ -49,5 +49,5 @@ def module_get(req, from:string) : tuple<string;string;string> const
4949
return [[auto mod_name, "{DAS_PAK_ROOT}/{join(rs,"/")}.das", alias_name]]
5050

5151
[export]
52-
def include_get(inc,from:string) : string
52+
def include_get(inc,_from:string) : string
5353
return starts_with(inc, "%") ? inc : "{DAS_PAK_ROOT}/{inc}"

prog/1stPartyLibs/daScript/das_config.h

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,18 +139,42 @@ inline size_t das_aligned_memsize(void *ptr) { return defaultmem->getSize(ptr);
139139
#include <ska_hash_map/flat_hash_map2.hpp>
140140
namespace das
141141
{
142-
template <typename K, typename V, typename H = das::hash<K>, typename E = das::equal_to<K>>
142+
// Default hasher for the das_* maps. Upstream defines daslang_hash in
143+
// das_hash_map.h (only used when DAS_CUSTOM_HASH is on). Dagor uses ska +
144+
// eastl::hash instead, so we provide our own daslang_hash that forwards to
145+
// das::hash (eastl::hash) for the general case. This keeps hashing identical to
146+
// before for every existing key, while letting headers specialize daslang_hash
147+
// for their own key types (e.g. AstSerializer's SerializeNodeId) and have that
148+
// specialization picked up as the map's hasher.
149+
template <typename T, typename Enable = void>
150+
struct daslang_hash {
151+
size_t operator()(const T &key) const noexcept { return das::hash<T>()(key); }
152+
};
153+
template <typename K, typename V, typename H = das::daslang_hash<K>, typename E = das::equal_to<K>>
143154
using das_map = ska::flat_hash_map<K, V, H, E>;
144-
template <typename K, typename H = das::hash<K>, typename E = das::equal_to<K>>
155+
template <typename K, typename H = das::daslang_hash<K>, typename E = das::equal_to<K>>
145156
using das_set = ska::flat_hash_set<K, H, E>;
146-
template <typename K, typename V, typename H = das::hash<K>, typename E = das::equal_to<K>>
157+
template <typename K, typename V, typename H = das::daslang_hash<K>, typename E = das::equal_to<K>>
147158
using das_hash_map = ska::flat_hash_map<K, V, H, E>;
148-
template <typename K, typename H = das::hash<K>, typename E = das::equal_to<K>>
159+
template <typename K, typename H = das::daslang_hash<K>, typename E = das::equal_to<K>>
149160
using das_hash_set = ska::flat_hash_set<K, H, E>;
150161
template <typename K, typename V>
151162
using das_safe_map = eastl::map<K, V>;
152163
template <typename K, typename C = das::less<K>>
153164
using das_safe_set = eastl::set<K, C>;
165+
// Insert-only / grow-only flavors. Dagor uses ska::flat_hash_map (no in-tree
166+
// daslang_insert_only_hash_map here), and DAS_CUSTOM_HASH is undefined, so these
167+
// must alias to the same type as das_hash_map/das_hash_set. The serializer and
168+
// das_common.h "ordered" insert-only overloads are guarded behind DAS_CUSTOM_HASH
169+
// precisely so that the regular das_hash_map overloads serve insert-only args.
170+
template <typename K, typename V, typename H = das::daslang_hash<K>, typename E = das::equal_to<K>>
171+
using das_insert_only_map = ska::flat_hash_map<K, V, H, E>;
172+
template <typename K, typename H = das::daslang_hash<K>, typename E = das::equal_to<K>>
173+
using das_insert_only_set = ska::flat_hash_set<K, H, E>;
174+
template <typename K, typename V, typename H = das::daslang_hash<K>, typename E = das::equal_to<K>>
175+
using das_insert_only_hash_map = ska::flat_hash_map<K, V, H, E>;
176+
template <typename K, typename H = das::daslang_hash<K>, typename E = das::equal_to<K>>
177+
using das_insert_only_hash_set = ska::flat_hash_set<K, H, E>;
154178
} // namespace das
155179

156180

0 commit comments

Comments
 (0)