Showing preview only (363K chars total). Download the full file or copy to clipboard to get everything.
Repository: nvpro-samples/gl_cadscene_rendertechniques
Branch: master
Commit: bd7e727c8b03
Files: 47
Total size: 347.6 KB
Directory structure:
gitextract_wdi1bw94/
├── .gitignore
├── CMakeLists.txt
├── CONTRIBUTING
├── LICENSE
├── README.md
├── cadscene.cpp
├── cadscene.hpp
├── common.h
├── csf.cpp
├── csfviewer.cpp
├── cull-bitpack.vert.glsl
├── cull-downsample.frag.glsl
├── cull-downsample.vert.glsl
├── cull-raster.frag.glsl
├── cull-raster.geo.glsl
├── cull-raster.vert.glsl
├── cull-tokencmds.vert.glsl
├── cull-tokensizes.vert.glsl
├── cull-xfb.vert.glsl
├── cullingsystem.cpp
├── cullingsystem.hpp
├── nodetree.cpp
├── nodetree.hpp
├── nvtoken.cpp
├── nvtoken.hpp
├── renderer.cpp
├── renderer.hpp
├── rendererindexedmdi.cpp
├── renderertoken.cpp
├── renderertokensortcull.cpp
├── renderertokenstream.cpp
├── rendereruborange.cpp
├── rendererubosub.cpp
├── scan.comp.glsl
├── scansystem.cpp
├── scansystem.hpp
├── scene.frag.glsl
├── scene.vert.glsl
├── statesystem.cpp
├── statesystem.hpp
├── tokenbase.cpp
├── tokenbase.hpp
├── transform-leaves.comp.glsl
├── transform-level.comp.glsl
├── transformsystem.cpp
├── transformsystem.hpp
└── xplode-animation.comp.glsl
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.clang-format
.editorconfig
#############################
#Spirv
#############################
*.spv
*.spva
*.sass
*.sassbin
*.bat
#############################
#specific to the project
#############################
cmake_built
cmake_build
build
_install
bin_x64
NVPRO_EXTERNAL
nvpro_core
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.5)
get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
Project(${PROJNAME})
Message(STATUS "-------------------------------")
Message(STATUS "Processing Project ${PROJNAME}:")
#####################################################################################
# look for nvpro_core 1) as a sub-folder 2) at some other locations
# this cannot be put anywhere else since we still didn't find setup.cmake yet
#
if(NOT BASE_DIRECTORY)
find_path(BASE_DIRECTORY
NAMES nvpro_core/cmake/setup.cmake
PATHS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../..
REQUIRED
DOC "Directory containing nvpro_core"
)
endif()
if(EXISTS ${BASE_DIRECTORY}/nvpro_core/cmake/setup.cmake)
include(${BASE_DIRECTORY}/nvpro_core/cmake/setup.cmake)
include(${BASE_DIRECTORY}/nvpro_core/cmake/utilities.cmake)
else()
message(FATAL_ERROR "could not find base directory, please set BASE_DIRECTORY to folder containing nvpro_core")
endif()
_add_project_definitions(${PROJNAME})
#--------------------------------------------------------------------------------------------------
# Resources
#
download_files(FILENAMES geforce.csf.gz)
#####################################################################################
# additions from packages needed for this sample
# add refs in LIBRARIES_OPTIMIZED
# add refs in LIBRARIES_DEBUG
# add files in PACKAGE_SOURCE_FILES
#
_add_package_OpenGL()
_add_package_ImGUI()
_add_package_ZLIB()
add_definitions(-DCSF_SUPPORT_ZLIB=1)
#####################################################################################
# process the rest of some cmake code that needs to be done *after* the packages add
_add_nvpro_core_lib()
#####################################################################################
# Source files for this project
#
file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c)
file(GLOB GLSL_FILES *.glsl)
#####################################################################################
# Executable
#
if(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
add_executable(${PROJNAME} ${SOURCE_FILES} ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES} ${GLSL_FILES})
#####################################################################################
# common source code needed for this sample
#
source_group(common FILES
${COMMON_SOURCE_FILES}
${PACKAGE_SOURCE_FILES}
)
source_group(shaders FILES
${GLSL_FILES}
)
#####################################################################################
# Linkage
#
target_link_libraries(${PROJNAME} ${PLATFORM_LIBRARIES} nvpro_core)
foreach(DEBUGLIB ${LIBRARIES_DEBUG})
target_link_libraries(${PROJNAME} debug ${DEBUGLIB})
endforeach(DEBUGLIB)
foreach(RELEASELIB ${LIBRARIES_OPTIMIZED})
target_link_libraries(${PROJNAME} optimized ${RELEASELIB})
endforeach(RELEASELIB)
#####################################################################################
# copies binaries that need to be put next to the exe files (ZLib, etc.)
#
_finalize_target( ${PROJNAME} )
LIST(APPEND GLSL_FILES "common.h")
install(FILES ${GLSL_FILES} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/GLSL_${PROJNAME}")
install(FILES ${GLSL_FILES} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/GLSL_${PROJNAME}")
================================================
FILE: CONTRIBUTING
================================================
https://developercertificate.org/
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
================================================
FILE: README.md
================================================
# gl cadscene render techniques
This sample implements several scene rendering techniques that target mostly static data, such as often found in CAD or DCC applications. In this context, 'static' means that the vertex and index buffers for the scene's objects rarely change. This can include editing the geometry of a few scene objects, but the matrix and material values are the properties that are modified the most across frames. Imagine making edits to the wheel topology of a car, or positioning an engine; the rest of the assembly remains the same.
The principal OpenGL mechanisms that are used here are described in the [SIGGRAPH 2014 presentation slides](http://on-demand.gputechconf.com/siggraph/2014/presentation/SG4117-OpenGL-Scene-Rendering-Techniques.pdf). It is highly recommended to go through the slides first.
The sample makes use of multiple OpenGL 4 core features, such as **ARB_multi_draw_indirect**, but also showcases OpenGL 3 style rendering techniques.
There are also several techniques built around the **NV_command_list** extension. Please refer to [gl commandlist basic](https://github.com/nvpro-samples/gl_commandlist_basic) for an introduction to NV_command_list.
> Note: This is just a sample to illustrate several techniques and possibilities for how to approach rendering. Its purpose is not to provide production-level, highly optimized implementations.
### Scene Setup
The sample loads a cadscene file (csf). This file format is inspired by CAD applications' data organization, but (for simplicity) everything is stored in a single RAW file.
The scene is organized into:
* Matrices: object transforms as well as concatenated world matrices
* TreeNodes: a tree consisting hierarchical information, mapping to Matrix indices
* Materials: just classic two-sided OpenGL Blinn-Phong material parameters
* Geometries: storing vertex and index information, organized into
* GeometryParts, which reference a sub-range within index buffer, for either "wireframe" or "solid" surfaces
* Objects, that reference Geometry and have corresponding
* ObjectParts, that encode part-level Material and Matrix assignment. Typically, an object uses just one Matrix for all its parts.
### Shademodes

- **solid**: only triangles are drawn
- **solid with edges**: triangles and edge outlines on top (using PolygonOffset to push triangles back). When no global sorting (see later) is performed, this means we toggle between the two modes for every object.
- **solid with edges (split test, only in sorted)**: an artificial mode that also separates triangles and edges into different FBOs, and is available in "sorted" and "token" renderers. The implementation has no real use-case character and is more or less for internal benchmarking of FBO toggles.
### Strategies
These influence the number of drawcalls we generate for the hardware and software. Using OpenGL's MultiDraw* functions we can have less software calls than hardware drawcalls, which helps trigger faster paths in the driver as there is less validation overhead. A strategy is applied on a per-object level.
Imagine an object whose parts use two materials, red and blue:
```
material: r b b r
parts: A B C D
```
- **materialgroups**
Here we create a per-object cache of drawcall ranges for MultiDraw* based on the object's material and matrix assignments. We also "grow" drawcalls if subsequent ranges in the index buffer have the same assignments. Our sample object would be drawn using 2 states one glMultiDrawElements each, which are creating 3 hardware drawcalls: red are ranges A, D and blue is B+C joined together as they are next to each other in the indexbuffer.
- **drawcall join**
As we traverse we combine drawcalls under same state, this means 3 drawcalls for hardware, and 3 for software as well as 3 states: red A, blue B+C, red D.
- **drawcall individual**
We render each piece individually:
red A, blue B, C, red D.
Typically we do all rendering with basic state redundancy filtering so we don't setup a matrix/material change if the same is still active. To keep things simple for state redundancy filtering, you should not go too fine-grained, otherwise all the tracking causes too much memory hopping. In our case we have 3 indices we track: geometry (handles vertex / index buffer setup), material, and matrix.
### Renderers
Most renderers will traverse the scene data every frame. The organization of the data is cache-friendly foremost, everything is stored in arrays, without too much memory hopping. Some renderers may implement additional caching for rendering.
#### Variants:
- **bindless**: these variants make use of NVIDIA's bindless extensions NV_vertex_buffer_unified_memory and NV_uniform_buffer_unified_memory, which allows a lower-overhead path in the driver for faster drawcall submission. Classic glBindVertexBuffer or glBindBufferRange are replaced with glBufferAddressRangeNV.
- **sorted**: indicates we do a global scene sort once, to minimize state changes in subsequent frames.
- **cullsorted**: next to global sorting by state, we also apply occlusion culling as presented in [end of the slides](http://on-demand.gputechconf.com/siggraph/2014/presentation/SG4117-OpenGL-Scene-Rendering-Techniques.pdf) or in the [gl occlusion culling](https://github.com/nvpro-samples/gl_occlusion_culling) sample.
- **emulated**: several of the NV_command_list techniques can be run in emulated mode.
#### Techniques:
We are mostly looking into accelerating our matrix and material parameter switching performance.
- **uborange**
All matrices and materials are stored in big buffer objects, which allows us to efficiently bind the required sub-range for a drawcall via glBindBufferRange(GL_UNIFORM_BUFFER, usageSlot, buffer, index * itemSize, itemSize). NVIDIA provides optimized paths if you keep the buffer and itemSize for a usageSlot constant for many glBindBufferRange calls. Be aware of GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, which is 256 bytes for most current NVIDIA hardware (Fermi, Kepler, Maxwell).
- **ubosub**
Not as efficient as the above, but maybe appropriate if you cannot afford to cache parameter data. We make use of one streaming buffer per usage slot and continously update it via glBufferSubData. NVIDIA's drivers do particularly well if you never bind this buffer as anything but a GL_UNIFORM_BUFFER and keep size and offsets a multiple of 4.
- **indexedmdi**
Similar to uborange we make use of all data stored in a bigger buffers in advance. It doesn't make this data "static"; you can always update the portions you need, but there is a high chance a lot of data is the same frame to frame. This time, we do not bind memory ranges through the OpenGL API, but let the shader do an indirection and only pass the required matrix and material indices.
For the matrix data we use GL_TEXTURE_BUFFER as it's particularly performant for high frequency / potentially divergent access. We typically have far more matrices than materials in our scene. For material data, it's a bit "ugly" to use lots of texelFetch instructions decoding all our parameters; it's much easier to write them as structs and store the array either as GL_UNIFORM_BUFFER or GL_SHADER_STORAGE_BUFFER. The latter is only recommended if you have divergent shader access or exceed the 64 KB limit of UBOs.
To pass the indices per-drawcall we make use of GL_ARB_multi_draw_indirect and "instanced" vertex attributes as described at [GTC 2013 on slide 27](http://on-demand.gputechconf.com/gtc/2013/presentations/S3032-Advanced-Scenegraph-Rendering-Pipeline.pdf).
Therefore this renderer requires two additional buffers: one encoding our object's matrix and material index assignments, and one encoding the scene's drawcalls as GL_DRAW_INDIRECT_BUFFER.
A hybrid approach, where the parameter index like "indexedmdi" is used for matrices and uborange bind is used for materials, is not yet implemented, but would be a good compromise.
The following renderers make use of the **NV_command_list** extension. In principle they **behave as "uborange"**, however all buffer bindings and drawcalls are encoded into binary tokens that are submitted in bulk. In preparation for drawing, the appropriate stateobjects are created and reused when rendering (one for lines and for triangles). While stateobject capturing is not extremely expensive, it is still best to cache it across frames.
- **tokenbuffer**
Similar to indexedmdi we create a buffer that describes our scene by storing all the relevant token commands. This buffer is filled only once and then later reused.
- **tokenlist**
Instead of storing the tokens inside a buffer we make use of the commandlist object, and create and compile one for each shademode for later reuse. Every time our state changes (for instance, when resizing FBOs), we have to recreate these lists, which makes it less flexible than buffer but faster when there are lots of statechanges within the list.
- **tokenstream**
This approach does not reuse the tokens across frames, but instead dynamically creates the tokenstream every frame. By default, the demo fills and submits tokens in chunks of 256 KB; better values may exist depending on the scene.
### Performance
All timings are preliminary results for *Timer Draw* on a win7-64, i7-860, Quadro K5000 system.
**Important Note About Timer Query Results:** The GPU time reported below is measured via timer queries, those values however can be skewed by CPU bottlenecks. The "begin" timestamp may be part of a different command submission to the GPU than the "end" timestamp. That means a long delay on the CPU side between those submissions will also increase the reported GPU time. That is why in CPU-bottlenecked scenarios with tons of OpenGL commands, the GPU times below are close to the CPU time.
```
scene statistics:
geometries: 110
materials: 66
nodes: 5004
objects: 2497
tokenbuffer/glstream complexities:
type: solid materialgroups | drawcall individual
commandsize: 347292 | 1301692
statetoggles: 1 | 1
tokens:
GL_DRAW_ELEMENTS_COMMAND_NV: 11103 | 68452
GL_ELEMENT_ADDRESS_COMMAND_NV: 807 | 807
GL_ATTRIBUTE_ADDRESS_COMMAND_NV: 807 | 807
GL_UNIFORM_ADDRESS_COMMAND_NV: 8988 | 11289
GL_POLYGON_OFFSET_COMMAND_NV: 1 | 1
type: solid w edges
commandsize: 629644 | 2534412
statetoggles: 4994 | 4994
tokens:
GL_DRAW_ELEMENTS_COMMAND_NV: 22281 | 136750
GL_ELEMENT_ADDRESS_COMMAND_NV: 807 | 807
GL_ATTRIBUTE_ADDRESS_COMMAND_NV: 807 | 807
GL_UNIFORM_ADDRESS_COMMAND_NV: 15457 | 20036
GL_POLYGON_OFFSET_COMMAND_NV: 1 | 1
```
As one can see from the statistics the key difference is the number of drawcalls for the hardware:
- **materialgroups**: ~ 10 000 drawcalls (inner two columns)
- **drawcall individual**: ~ 70 000 drawcalls (rightmost two columns)
*shademode: solid*
renderer | GPU time | CPU time | GPU time | CPU time (microseconds)
------------ | ------------- | ------------- | ------------- | -------------
**strategy** | **material-** | **-groups** | **drawcall-** | **-individual**
ubosub | 1550 | 1870 | 6000 | 7420
uborange | 1010| 1890 | 3720 | 7660
uborange_bindless | 1010 | 1200 | 2560 | 4900
indexedmdi | 1120 | 1200 | 2080 | 1100
tokenstream | 860 | 300 | 1520 | 1400
tokenbuffer | 780 | <10 | 1230 | <10
tokenlist | 780 | <10 | 880 | <10
tokenbuffer_cullsorted | 540 | 120 | 760 | 120
The results are of course very scene dependent; this model was specifically chosen as it is made of many parts with very few triangles. If the complexity per drawcall were higher (say more triangles or complex shading), then the CPU impact would be lower and we would be GPU-bound. However the CPU time recovered by faster submission mechanisms can always be used elsewhere. So even if we are GPU-bound, time should not be wasted.
We can see that the "token" techniques do very well and are never CPU-bound, and the "indexedmdi" technique is also quite good. This technique is especially useful for very high-frequency parameters, for example when rendering "id-buffers" for selection, but also for matrix indices. For general use-cases, working with uborange binds is recommended.
*shademode: solid with edges*
Unless "sorted", around 5000 toggles are done between triangles/line rendering. The shader
is manipulated through an immediate vertex attribute to toggle between lit/unlit rendering respectively.
renderer | GPU time | CPU time | GPU time | CPU time (microseconds)
------------ | ------------- | ------------- | ------------- | -------------
**strategy** | **material-** | **-groups** | **drawcall-** | **-individual**
ubosub | 2890 | 3350 | 13000 | 15000 |
uborange | 2150 | 3700 | 12500 | 15200 |
uborange_bindless | 2150 | 2640 | 8300 | 10000
indexedmdi | 2340 | 2200 | 4050 | 2050
tokenstream | 1860 | 1250 | 3360 | 3200
tokenbuffer | 1750 | 450 | 2650 | 350
tokenlist | 1650 | <10 | 1890 | <10
tokenbuffer_cullsorted | 770 | 120 | 1250 | 120
Compared to the "solid" results, the tokenbuffer and tokenlist techniques show a greater difference in CPU time.
### Model Explosion View
The simple viewer allows you to add animation to the scene and artificially increase scene complexity via "clones".

To "emulate" typical interaction where users might move objects around or have animated scenes, the sample also implements the matrix transform system sketched on [slide 30](http://on-demand.gputechconf.com/siggraph/2014/presentation/SG4117-OpenGL-Scene-Rendering-Techniques.pdf).
The effect works by first moving all object matrices a bit (*xplode-animation.comp.glsl*), and afterwards the transform hierarchy is updated via a system that is implemented in the *transformsystem.cpp / hpp* files.
The code is not particularly tuned but naively assumes that upper levels of the hierarchy contain fewer nodes than lower levels (pyramid). Therefore it uses leaf-processing (which redundantly calculates matrices) instead of level-wise processing for the first 10 levels, to avoid dependencies (one small compute task waiting for the previous). Later levels are always processed level-wise. A better strategy would be to switch between the two approaches based on the actual number of nodes per level. The shaders for this are *transform-leaves.comp.glsl* and *transform-level.comp.glsl*.
The hierarchy is managed by *nodetree.cpp/hpp*, which stores the tree as array of 32bit values. Each value represents a node, and encodes the "level" in the hierarchy in 8 bits and their parent index in the rest of the bits. Which means you can traverse a node up to the root:
``` cpp
// sample traversal of "idx" node to root
self = array[idx];
while( self.level != 0) {
self = array[self.parent];
}
// self is now the top root for the idx node
```
The nodetree also stores two node index lists for each level: one storing all nodes of a level, and one for all leaves in this level. We feed these two index lists to the appropriate shader. When leaf processing is used we append the leaves level-wise, which should minimize divergence within a warp (ideally most threads have the same number of levels to ascend in the hierarchy).
Many CAD applications tend to use double-precision matrices, and the system could be adjusted for this. For rendering, however, float matrices should be used. To account for large translation values, one could run a concatenation of view-projection (double) and object-world-matrix (double) per-frame and generate the matrices (float) for actual vertex transforms. To improve memory performance, it might be beneficial to use double only for storing translations within the matrices.
> Note: Only the GPU matrices are updated. CPU techniques such as "ubosub" will not show animations.
### Sample Highlights
This sample is a bit more complex than most others as it contains several subsystems. Don't hesitate to contact the author if something is unclear (commenting was not a priority ;) ).
#### csfviewer.cpp
The principle setup of the sample is in this main file. However, most of the interesting bits happen in the renderers.
- Sample::think - prepares the frame and calls the renderer's draw function
#### renderer... and tokenbase...
Each renderer has its own file and is derived from the **Renderer** class in *renderer.hpp*
- Renderer::init - some renderers may allocate extra buffers or create their own data structures for the scene.
- Renderer::deinit
- Renderer::draw
The renderers may have additional functions. The "token" renderers using NV_command_list or "indexedmdi", for instance, must create their own scene representation.
#### cadscene...
The "csf" (cadscene file) format is a simple binary format that encodes a scene as is typical for CAD. It closely matches the description at the beginning of the readme. It is not very sophisticated, and is meant for demo purposes.
> *Note*: The **geforce.csf.gz** assembly binary file that ships with this sample **may NOT be redistributed.**
#### nodetree... and transform...
Implement the matrix hierarchy updates as described in the "model explosion view" section.
#### cull... and scan...
For files related to culling, it is best to refer to the [gl occlusion cullling](https://github.com/nvpro-samples/gl_occlusion_cullling) sample, as it leverages the same system and focuses on just that topic.
*renderertokensortcull.cpp* implements *RendererCullSortToken::CullJobToken::resultFromBits*, which contains the details of how the occlusion results are handled in this sample. The implementation uses the "raster" "temporal" approach.
#### statesystem... nvtoken... and nvcommandlist...
These files contain helpers when using the NV_command_list extension. Please see [gl commandlist basic](https://github.com/nvpro-samples/gl_commandlist_basic) for a smaller sample.
### Building
Ideally, clone this and other interesting [nvpro-samples](https://github.com/nvpro-samples) repositories into a common subdirectory. You will always need [nvpro_core](https://github.com/nvpro-samples/nvpro_core). The nvpro_core is searched either as a subdirectory of the sample, or one directory up.
If you are interested in multiple samples, you can use the [build_all](https://github.com/nvpro-samples/build_all) CMAKE as entry point. This will also give you options to enable or disable individual samples when creating the solutions.
### Related Samples
[gl commandlist basic](https://github.com/nvpro-samples/gl_commandlist_basic) illustrates the core principle of the NV_command_list extension.
[gl occlusion cullling](https://github.com/nvpro-samples/gl_occlusion_cullling) also uses the occlusion system of this sample, but in a simpler usage scenario.
When using classic scenegraphs, there is typically a lot of overhead in traversing the scene. For this reason, it is highly recommended to use simpler representations for actual rendering. Consider using flattened hierarchies, arrays, memory-friendly data structures, data-oriented design patterns, and similar techniques.
If you are still working with a classic scenegraph, then [nvpro-pipeline](https://github.com/nvpro-pipeline/pipeline) may provide some acceleration strategies to avoid full scenegraph traversal. Some of these strategies are also described in this [GTC 2013 presentation](http://on-demand.gputechconf.com/gtc/2013/presentations/S3032-Advanced-Scenegraph-Rendering-Pipeline.pdf).
================================================
FILE: cadscene.cpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#include "cadscene.hpp"
#include <fileformats/cadscenefile.h>
#include <algorithm>
#include <assert.h>
#include <cstddef>
#include "glm/gtc/type_ptr.hpp"
#define USE_CACHECOMBINE 1
glm::vec4 randomVector(float from, float to)
{
glm::vec4 vec;
float width = to - from;
for(int i = 0; i < 4; i++)
{
vec[i] = from + (float(rand()) / float(RAND_MAX)) * width;
}
return vec;
}
static void recursiveHierarchy(NodeTree& tree, CSFile* csf, int idx, int cloneoffset)
{
for(int i = 0; i < csf->nodes[idx].numChildren; i++)
{
tree.setNodeParent((NodeTree::nodeID)csf->nodes[idx].children[i] + cloneoffset, (NodeTree::nodeID)idx + cloneoffset);
}
for(int i = 0; i < csf->nodes[idx].numChildren; i++)
{
recursiveHierarchy(tree, csf, csf->nodes[idx].children[i], cloneoffset);
}
}
bool CadScene::loadCSF(const char* filename, int clones, int cloneaxis)
{
CSFile* csf;
CSFileMemoryPTR mem = CSFileMemory_new();
if(CSFile_loadExt(&csf, filename, mem) != CADSCENEFILE_NOERROR || !(csf->fileFlags & CADSCENEFILE_FLAG_UNIQUENODES))
{
CSFileMemory_delete(mem);
return false;
}
int copies = clones + 1;
CSFile_transform(csf);
srand(234525);
// materials
m_materials.resize(csf->numMaterials);
for(int n = 0; n < csf->numMaterials; n++)
{
CSFMaterial* csfmaterial = &csf->materials[n];
Material& material = m_materials[n];
for(int i = 0; i < 2; i++)
{
material.sides[i].ambient = randomVector(0.0f, 0.1f);
material.sides[i].diffuse = glm::make_vec4(csf->materials[n].color) + randomVector(0.0f, 0.07f);
material.sides[i].specular = randomVector(0.25f, 0.55f);
material.sides[i].emissive = randomVector(0.0f, 0.05f);
}
}
glCreateBuffers(1, &m_materialsGL);
glNamedBufferStorage(m_materialsGL, sizeof(Material) * m_materials.size(), &m_materials[0], 0);
//glMapNamedBufferRange(m_materialsGL, 0, sizeof(Material) * m_materials.size(), GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT);
// geometry
int numGeoms = csf->numGeometries;
m_geometry.resize(csf->numGeometries * copies);
m_geometryBboxes.resize(csf->numGeometries * copies);
for(int n = 0; n < csf->numGeometries; n++)
{
CSFGeometry* csfgeom = &csf->geometries[n];
Geometry& geom = m_geometry[n];
geom.cloneIdx = -1;
geom.numVertices = csfgeom->numVertices;
geom.numIndexSolid = csfgeom->numIndexSolid;
geom.numIndexWire = csfgeom->numIndexWire;
std::vector<Vertex> vertices(csfgeom->numVertices);
for(int i = 0; i < csfgeom->numVertices; i++)
{
vertices[i].position[0] = csfgeom->vertex[3 * i + 0];
vertices[i].position[1] = csfgeom->vertex[3 * i + 1];
vertices[i].position[2] = csfgeom->vertex[3 * i + 2];
vertices[i].position[3] = 1.0f;
if(csfgeom->normal)
{
vertices[i].normal[0] = csfgeom->normal[3 * i + 0];
vertices[i].normal[1] = csfgeom->normal[3 * i + 1];
vertices[i].normal[2] = csfgeom->normal[3 * i + 2];
vertices[i].normal[3] = 0.0f;
}
else
{
vertices[i].normal = glm::vec4(normalize(glm::vec3(vertices[i].position)), 0.0f);
}
m_geometryBboxes[n].merge(vertices[i].position);
}
geom.vboSize = sizeof(Vertex) * vertices.size();
glCreateBuffers(1, &geom.vboGL);
glNamedBufferStorage(geom.vboGL, geom.vboSize, &vertices[0], 0);
std::vector<GLuint> indices(csfgeom->numIndexSolid + csfgeom->numIndexWire);
memcpy(&indices[0], csfgeom->indexSolid, sizeof(GLuint) * csfgeom->numIndexSolid);
if(csfgeom->indexWire)
{
memcpy(&indices[csfgeom->numIndexSolid], csfgeom->indexWire, sizeof(GLuint) * csfgeom->numIndexWire);
}
geom.iboSize = sizeof(GLuint) * indices.size();
glCreateBuffers(1, &geom.iboGL);
glNamedBufferStorage(geom.iboGL, geom.iboSize, &indices[0], 0);
if(has_GL_NV_vertex_buffer_unified_memory)
{
glGetNamedBufferParameterui64vNV(geom.vboGL, GL_BUFFER_GPU_ADDRESS_NV, &geom.vboADDR);
glMakeNamedBufferResidentNV(geom.vboGL, GL_READ_ONLY);
glGetNamedBufferParameterui64vNV(geom.iboGL, GL_BUFFER_GPU_ADDRESS_NV, &geom.iboADDR);
glMakeNamedBufferResidentNV(geom.iboGL, GL_READ_ONLY);
}
geom.parts.resize(csfgeom->numParts);
size_t offsetSolid = 0;
size_t offsetWire = csfgeom->numIndexSolid * sizeof(GLuint);
for(int i = 0; i < csfgeom->numParts; i++)
{
geom.parts[i].indexWire.count = csfgeom->parts[i].numIndexWire;
geom.parts[i].indexSolid.count = csfgeom->parts[i].numIndexSolid;
geom.parts[i].indexWire.offset = offsetWire;
geom.parts[i].indexSolid.offset = offsetSolid;
offsetWire += csfgeom->parts[i].numIndexWire * sizeof(GLuint);
offsetSolid += csfgeom->parts[i].numIndexSolid * sizeof(GLuint);
}
}
for(int c = 1; c <= clones; c++)
{
for(int n = 0; n < numGeoms; n++)
{
m_geometryBboxes[n + numGeoms * c] = m_geometryBboxes[n];
const Geometry& geomorig = m_geometry[n];
Geometry& geom = m_geometry[n + numGeoms * c];
geom = geomorig;
#if 1
geom.cloneIdx = n;
#else
geom.cloneIdx = -1;
glCreateBuffers(1, &geom.vboGL);
glNamedBufferStorage(geom.vboGL, geom.vboSize, 0, 0);
glCreateBuffers(1, &geom.iboGL);
glNamedBufferStorage(geom.iboGL, geom.iboSize, 0, 0);
if(has_GL_NV_vertex_buffer_unified_memory)
{
glGetNamedBufferParameterui64vNV(geom.vboGL, GL_BUFFER_GPU_ADDRESS_NV, &geom.vboADDR);
glMakeNamedBufferResidentNV(geom.vboGL, GL_READ_ONLY);
glGetNamedBufferParameterui64vNV(geom.iboGL, GL_BUFFER_GPU_ADDRESS_NV, &geom.iboADDR);
glMakeNamedBufferResidentNV(geom.iboGL, GL_READ_ONLY);
}
glCopyNamedBufferSubData(geomorig.vboGL, geom.vboGL, 0, 0, geom.vboSize);
glCopyNamedBufferSubData(geomorig.iboGL, geom.iboGL, 0, 0, geom.iboSize);
#endif
}
}
glCreateBuffers(1, &m_geometryBboxesGL);
glNamedBufferStorage(m_geometryBboxesGL, sizeof(BBox) * m_geometryBboxes.size(), &m_geometryBboxes[0], 0);
glCreateTextures(GL_TEXTURE_BUFFER, 1, &m_geometryBboxesTexGL);
glTextureBuffer(m_geometryBboxesTexGL, GL_RGBA32F, m_geometryBboxesGL);
// nodes
int numObjects = 0;
m_matrices.resize(csf->numNodes * copies);
for(int n = 0; n < csf->numNodes; n++)
{
CSFNode* csfnode = &csf->nodes[n];
memcpy(glm::value_ptr(m_matrices[n].objectMatrix), csfnode->objectTM, sizeof(float) * 16);
memcpy(glm::value_ptr(m_matrices[n].worldMatrix), csfnode->worldTM, sizeof(float) * 16);
m_matrices[n].objectMatrixIT = glm::transpose(glm::inverse(m_matrices[n].objectMatrix));
m_matrices[n].worldMatrixIT = glm::transpose(glm::inverse(m_matrices[n].worldMatrix));
if(csfnode->geometryIDX < 0)
continue;
numObjects++;
}
// objects
m_objects.resize(numObjects * copies);
m_objectAssigns.resize(numObjects * copies);
numObjects = 0;
for(int n = 0; n < csf->numNodes; n++)
{
CSFNode* csfnode = &csf->nodes[n];
if(csfnode->geometryIDX < 0)
continue;
Object& object = m_objects[numObjects];
object.matrixIndex = n;
object.geometryIndex = csfnode->geometryIDX;
m_objectAssigns[numObjects] = glm::ivec2(object.matrixIndex, object.geometryIndex);
object.parts.resize(csfnode->numParts);
for(int i = 0; i < csfnode->numParts; i++)
{
object.parts[i].active = 1;
object.parts[i].matrixIndex = csfnode->parts[i].nodeIDX < 0 ? object.matrixIndex : csfnode->parts[i].nodeIDX;
object.parts[i].materialIndex = csfnode->parts[i].materialIDX;
}
BBox bbox = m_geometryBboxes[object.geometryIndex].transformed(m_matrices[n].worldMatrix);
m_bbox.merge(bbox);
updateObjectDrawCache(object);
numObjects++;
}
// compute clone move delta based on m_bbox;
glm::vec4 dim = m_bbox.max - m_bbox.min;
int sq = 1;
int numAxis = 0;
for(int i = 0; i < 3; i++)
{
numAxis += (cloneaxis & (1 << i)) ? 1 : 0;
}
assert(numAxis);
switch(numAxis)
{
case 1:
sq = copies;
break;
case 2:
while(sq * sq < copies)
{
sq++;
}
break;
case 3:
while(sq * sq * sq < copies)
{
sq++;
}
break;
}
for(int c = 1; c <= clones; c++)
{
int numNodes = csf->numNodes;
glm::vec4 shift = dim * 1.05f;
float u = 0;
float v = 0;
float w = 0;
switch(numAxis)
{
case 1:
u = float(c);
break;
case 2:
u = float(c % sq);
v = float(c / sq);
break;
case 3:
u = float(c % sq);
v = float((c / sq) % sq);
w = float(c / (sq * sq));
break;
}
float use = u;
if(cloneaxis & (1 << 0))
{
shift.x *= -use;
if(numAxis > 1)
use = v;
}
else
{
shift.x = 0;
}
if(cloneaxis & (1 << 1))
{
shift.y *= use;
if(numAxis > 2)
use = w;
else if(numAxis > 1)
use = v;
}
else
{
shift.y = 0;
}
if(cloneaxis & (1 << 2))
{
shift.z *= -use;
}
else
{
shift.z = 0;
}
shift.w = 0;
// move all world matrices
for(int n = 0; n < numNodes; n++)
{
MatrixNode& node = m_matrices[n + numNodes * c];
MatrixNode& nodeOrig = m_matrices[n];
node = nodeOrig;
node.worldMatrix[3] = node.worldMatrix[3] + shift;
node.worldMatrixIT = glm::transpose(glm::inverse(node.worldMatrix));
}
{
// patch object matrix of root
MatrixNode& node = m_matrices[csf->rootIDX + numNodes * c];
node.objectMatrix[3] = node.objectMatrix[3] + shift;
node.objectMatrixIT = glm::transpose(glm::inverse(node.objectMatrix));
}
// clone objects
for(int n = 0; n < numObjects; n++)
{
const Object& objectorig = m_objects[n];
Object& object = m_objects[n + numObjects * c];
object = objectorig;
object.geometryIndex += c * numGeoms;
object.matrixIndex += c * numNodes;
for(size_t i = 0; i < object.parts.size(); i++)
{
object.parts[i].matrixIndex += c * numNodes;
}
for(size_t i = 0; i < object.cacheSolid.state.size(); i++)
{
object.cacheSolid.state[i].matrixIndex += c * numNodes;
}
for(size_t i = 0; i < object.cacheWire.state.size(); i++)
{
object.cacheWire.state[i].matrixIndex += c * numNodes;
}
m_objectAssigns[n + numObjects * c] = glm::ivec2(object.matrixIndex, object.geometryIndex);
}
}
glCreateBuffers(1, &m_matricesGL);
glNamedBufferStorage(m_matricesGL, sizeof(MatrixNode) * m_matrices.size(), &m_matrices[0], 0);
//glMapNamedBufferRange(m_matricesGL, 0, sizeof(MatrixNode) * m_matrices.size(), GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT);
glCreateTextures(GL_TEXTURE_BUFFER, 1, &m_matricesTexGL);
glTextureBuffer(m_matricesTexGL, GL_RGBA32F, m_matricesGL);
glCreateBuffers(1, &m_objectAssignsGL);
glNamedBufferStorage(m_objectAssignsGL, sizeof(glm::ivec2) * m_objectAssigns.size(), &m_objectAssigns[0], 0);
if(has_GL_NV_vertex_buffer_unified_memory)
{
glGetNamedBufferParameterui64vNV(m_materialsGL, GL_BUFFER_GPU_ADDRESS_NV, &m_materialsADDR);
glMakeNamedBufferResidentNV(m_materialsGL, GL_READ_ONLY);
glGetNamedBufferParameterui64vNV(m_matricesGL, GL_BUFFER_GPU_ADDRESS_NV, &m_matricesADDR);
glMakeNamedBufferResidentNV(m_matricesGL, GL_READ_ONLY);
if(has_GL_ARB_bindless_texture)
{
m_matricesTexGLADDR = glGetTextureHandleARB(m_matricesTexGL);
glMakeTextureHandleResidentARB(m_matricesTexGLADDR);
}
}
m_nodeTree.create(copies * csf->numNodes);
for(int i = 0; i < copies; i++)
{
int cloneoffset = (csf->numNodes) * i;
int root = csf->rootIDX + cloneoffset;
recursiveHierarchy(m_nodeTree, csf, csf->rootIDX, cloneoffset);
m_nodeTree.setNodeParent((NodeTree::nodeID)root, m_nodeTree.getTreeRoot());
m_nodeTree.addToTree((NodeTree::nodeID)root);
}
glCreateBuffers(1, &m_parentIDsGL);
glNamedBufferStorage(m_parentIDsGL, m_nodeTree.getTreeCompactNodes().size() * sizeof(GLuint),
&m_nodeTree.getTreeCompactNodes()[0], 0);
glCreateBuffers(1, &m_matricesOrigGL);
glNamedBufferStorage(m_matricesOrigGL, sizeof(MatrixNode) * m_matrices.size(), &m_matrices[0], 0);
glCreateTextures(GL_TEXTURE_BUFFER, 1, &m_matricesOrigTexGL);
glTextureBuffer(m_matricesOrigTexGL, GL_RGBA32F, m_matricesOrigGL);
CSFileMemory_delete(mem);
return true;
}
struct ListItem
{
CadScene::DrawStateInfo state;
CadScene::DrawRange range;
};
static bool ListItem_compare(const ListItem& a, const ListItem& b)
{
int diff = 0;
diff = diff != 0 ? diff : (a.state.materialIndex - b.state.materialIndex);
diff = diff != 0 ? diff : (a.state.matrixIndex - b.state.matrixIndex);
diff = diff != 0 ? diff : int(a.range.offset - b.range.offset);
return diff < 0;
}
static void fillCache(CadScene::DrawRangeCache& cache, const std::vector<ListItem>& list)
{
cache = CadScene::DrawRangeCache();
if(!list.size())
return;
CadScene::DrawStateInfo state = list[0].state;
CadScene::DrawRange range = list[0].range;
int stateCount = 0;
for(size_t i = 1; i < list.size() + 1; i++)
{
bool newrange = false;
if(i == list.size() || list[i].state != state)
{
// push range
stateCount++;
cache.offsets.push_back(range.offset);
cache.counts.push_back(range.count);
// emit
cache.state.push_back(state);
cache.stateCount.push_back(stateCount);
stateCount = 0;
if(i == list.size())
{
break;
}
else
{
state = list[i].state;
range.offset = list[i].range.offset;
range.count = 0;
newrange = true;
}
}
const CadScene::DrawRange& currange = list[i].range;
if(newrange || (USE_CACHECOMBINE && currange.offset == (range.offset + sizeof(GLuint) * range.count)))
{
// merge
range.count += currange.count;
}
else
{
// push
stateCount++;
cache.offsets.push_back(range.offset);
cache.counts.push_back(range.count);
range = currange;
}
}
}
void CadScene::updateObjectDrawCache(Object& object)
{
Geometry& geom = m_geometry[object.geometryIndex];
std::vector<ListItem> listSolid;
std::vector<ListItem> listWire;
listSolid.reserve(geom.parts.size());
listWire.reserve(geom.parts.size());
for(size_t i = 0; i < geom.parts.size(); i++)
{
if(!object.parts[i].active)
continue;
ListItem item;
item.state.materialIndex = object.parts[i].materialIndex;
item.range = geom.parts[i].indexSolid;
item.state.matrixIndex = object.parts[i].matrixIndex;
listSolid.push_back(item);
item.range = geom.parts[i].indexWire;
item.state.matrixIndex = object.parts[i].matrixIndex;
listWire.push_back(item);
}
std::sort(listSolid.begin(), listSolid.end(), ListItem_compare);
std::sort(listWire.begin(), listWire.end(), ListItem_compare);
fillCache(object.cacheSolid, listSolid);
fillCache(object.cacheWire, listWire);
}
void CadScene::enableVertexFormat(int attrPos, int attrNormal)
{
glVertexAttribFormat(attrPos, 3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(attrNormal, 3, GL_FLOAT, GL_FALSE, offsetof(CadScene::Vertex, normal));
glVertexAttribBinding(attrPos, 0);
glVertexAttribBinding(attrNormal, 0);
glEnableVertexAttribArray(attrPos);
glEnableVertexAttribArray(attrNormal);
glBindVertexBuffer(0, 0, 0, sizeof(CadScene::Vertex));
}
void CadScene::disableVertexFormat(int attrPos, int attrNormal)
{
glDisableVertexAttribArray(attrPos);
glDisableVertexAttribArray(attrNormal);
glBindVertexBuffer(0, 0, 0, sizeof(CadScene::Vertex));
}
void CadScene::unload()
{
if(m_geometry.empty())
return;
glFinish();
if(has_GL_NV_vertex_buffer_unified_memory)
{
if(has_GL_ARB_bindless_texture)
{
glMakeTextureHandleNonResidentARB(m_matricesTexGLADDR);
}
glMakeNamedBufferNonResidentNV(m_matricesGL);
glMakeNamedBufferNonResidentNV(m_materialsGL);
}
glDeleteTextures(1, &m_matricesOrigTexGL);
glDeleteTextures(1, &m_matricesTexGL);
glDeleteTextures(1, &m_geometryBboxesTexGL);
glDeleteBuffers(1, &m_matricesOrigGL);
glDeleteBuffers(1, &m_matricesGL);
glDeleteBuffers(1, &m_materialsGL);
glDeleteBuffers(1, &m_objectAssignsGL);
glDeleteBuffers(1, &m_geometryBboxesGL);
glDeleteBuffers(1, &m_parentIDsGL);
for(size_t i = 0; i < m_geometry.size(); i++)
{
if(m_geometry[i].cloneIdx >= 0)
continue;
if(has_GL_NV_vertex_buffer_unified_memory)
{
glMakeNamedBufferNonResidentNV(m_geometry[i].iboGL);
glMakeNamedBufferNonResidentNV(m_geometry[i].vboGL);
}
glDeleteBuffers(1, &m_geometry[i].iboGL);
glDeleteBuffers(1, &m_geometry[i].vboGL);
}
m_matrices.clear();
m_geometryBboxes.clear();
m_geometry.clear();
m_objectAssigns.clear();
m_objects.clear();
m_geometryBboxes.clear();
m_nodeTree.clear();
glFinish();
}
void CadScene::resetMatrices()
{
glCopyNamedBufferSubData(m_matricesOrigGL, m_matricesGL, 0, 0, sizeof(CadScene::MatrixNode) * m_matrices.size());
}
================================================
FILE: cadscene.hpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef CADSCENE_H__
#define CADSCENE_H__
#include <cstring> // memset
#include <nvgl/extensions_gl.hpp>
#include <glm/glm.hpp>
#include <vector>
#include "nodetree.hpp"
class CadScene {
public:
struct BBox {
glm::vec4 min;
glm::vec4 max;
BBox() : min(FLT_MAX), max(-FLT_MAX) {}
inline void merge( const glm::vec4& point )
{
min = glm::min(min, point);
max = glm::max(max, point);
}
inline void merge( const BBox& bbox )
{
min = glm::min(min, bbox.min);
max = glm::max(max, bbox.max);
}
inline BBox transformed ( const glm::mat4 &matrix, int dim=3)
{
int i;
glm::vec4 box[16];
// create box corners
box[0] = glm::vec4(min.x,min.y,min.z,min.w);
box[1] = glm::vec4(max.x,min.y,min.z,min.w);
box[2] = glm::vec4(min.x,max.y,min.z,min.w);
box[3] = glm::vec4(max.x,max.y,min.z,min.w);
box[4] = glm::vec4(min.x,min.y,max.z,min.w);
box[5] = glm::vec4(max.x,min.y,max.z,min.w);
box[6] = glm::vec4(min.x,max.y,max.z,min.w);
box[7] = glm::vec4(max.x,max.y,max.z,min.w);
box[8] = glm::vec4(min.x,min.y,min.z,max.w);
box[9] = glm::vec4(max.x,min.y,min.z,max.w);
box[10] = glm::vec4(min.x,max.y,min.z,max.w);
box[11] = glm::vec4(max.x,max.y,min.z,max.w);
box[12] = glm::vec4(min.x,min.y,max.z,max.w);
box[13] = glm::vec4(max.x,min.y,max.z,max.w);
box[14] = glm::vec4(min.x,max.y,max.z,max.w);
box[15] = glm::vec4(max.x,max.y,max.z,max.w);
// transform box corners
// and find new mins,maxs
BBox bbox;
for (i = 0; i < (1<<dim) ; i++){
glm::vec4 point = matrix * box[i];
bbox.merge(point);
}
return bbox;
}
};
struct MaterialSide {
glm::vec4 ambient;
glm::vec4 diffuse;
glm::vec4 specular;
glm::vec4 emissive;
};
// need to keep this 256 byte aligned (UBO range)
struct Material {
MaterialSide sides[2];
GLuint64 texturesADDR[4];
GLuint textures[4];
GLuint _pad[4+16];
Material() {
memset(this,0,sizeof(Material));
}
};
// need to keep this 256 byte aligned (UBO range)
struct MatrixNode {
glm::mat4 worldMatrix;
glm::mat4 worldMatrixIT;
glm::mat4 objectMatrix;
glm::mat4 objectMatrixIT;
};
struct Vertex {
glm::vec4 position;
glm::vec4 normal;
};
struct DrawRange {
size_t offset;
int count;
DrawRange() : offset(0) , count(0) {}
};
struct DrawStateInfo {
int materialIndex;
int matrixIndex;
friend bool operator != ( const DrawStateInfo &lhs, const DrawStateInfo &rhs){
return lhs.materialIndex != rhs.materialIndex || lhs.matrixIndex != rhs.matrixIndex;
}
friend bool operator == ( const DrawStateInfo &lhs, const DrawStateInfo &rhs){
return lhs.materialIndex == rhs.materialIndex && lhs.matrixIndex == rhs.matrixIndex;
}
};
struct DrawRangeCache {
std::vector<DrawStateInfo> state;
std::vector<int> stateCount;
std::vector<size_t> offsets;
std::vector<int> counts;
};
struct GeometryPart {
DrawRange indexSolid;
DrawRange indexWire;
};
struct Geometry {
GLuint vboGL;
GLuint iboGL;
GLuint64 vboADDR;
GLuint64 iboADDR;
size_t vboSize;
size_t iboSize;
std::vector<GeometryPart> parts;
int numVertices;
int numIndexSolid;
int numIndexWire;
int cloneIdx;
};
struct ObjectPart {
int active;
int materialIndex;
int matrixIndex;
};
struct Object {
int matrixIndex;
int geometryIndex;
std::vector<ObjectPart> parts;
DrawRangeCache cacheSolid;
DrawRangeCache cacheWire;
};
std::vector<Material> m_materials;
std::vector<BBox> m_geometryBboxes;
std::vector<Geometry> m_geometry;
std::vector<MatrixNode> m_matrices;
std::vector<Object> m_objects;
std::vector<glm::ivec2> m_objectAssigns;
BBox m_bbox;
GLuint m_materialsGL;
GLuint64 m_materialsADDR;
GLuint m_matricesGL;
GLuint64 m_matricesADDR;
GLuint m_matricesTexGL;
GLuint64 m_matricesTexGLADDR;
GLuint m_geometryBboxesGL;
GLuint m_geometryBboxesTexGL;
GLuint m_objectAssignsGL;
GLuint m_parentIDsGL;
GLuint m_matricesOrigGL;
GLuint m_matricesOrigTexGL;
NodeTree m_nodeTree;
void updateObjectDrawCache(Object& object);
bool loadCSF(const char* filename, int clones = 0, int cloneaxis=3);
void unload();
static void enableVertexFormat(int attrPos, int attrNormal);
static void disableVertexFormat(int attrPos, int attrNormal);
void resetMatrices();
};
#endif
================================================
FILE: common.h
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#define VERTEX_POS 0
#define VERTEX_NORMAL 1
#define VERTEX_ASSIGNS 2
#define VERTEX_WIREMODE 3
#define UBO_SCENE 0
#define UBO_MATRIX 1
#define UBO_MATERIAL 2
#define TEX_MATRICES 0
#define USE_BASEINSTANCE 0
//#define UNI_WIREFRAME 0
#ifdef __cplusplus
namespace csfviewer
{
using namespace glm;
#endif
struct SceneData {
mat4 viewProjMatrix;
mat4 viewMatrix;
mat4 viewMatrixIT;
vec4 viewPos;
vec4 viewDir;
vec4 wLightPos;
ivec2 viewport;
uvec2 tboMatrices;
};
#ifdef __cplusplus
}
#endif
#if defined(GL_core_profile) || defined(GL_compatibility_profile) || defined(GL_es_profile)
#extension GL_NV_command_list : enable
#if GL_NV_command_list
layout(commandBindableNV) uniform;
#endif
// prevent this to be used by c++
layout(std140,binding=UBO_SCENE) uniform sceneBuffer {
SceneData scene;
};
// must match cadscene!
layout(std140,binding=UBO_MATRIX) uniform matrixBuffer {
mat4 worldMatrix;
mat4 worldMatrixIT;
mat4 objectMatrix;
mat4 objectMatrixIT;
} object;
#extension GL_ARB_bindless_texture : enable
#extension GL_NV_bindless_texture : enable
#if GL_NV_bindless_texture
#define matricesBuffer samplerBuffer(scene.tboMatrices)
#else
layout(binding=TEX_MATRICES) uniform samplerBuffer matricesBuffer;
#endif
// must match cadscene!
#define NODE_MATRIX_WORLD 0
#define NODE_MATRIX_WORLDIT 1
#define NODE_MATRIX_OBJECT 2
#define NODE_MATRIX_OBJECTIT 3
#define NODE_MATRICES 4
mat4 getIndexedMatrix(int idx, int what)
{
int i = idx * NODE_MATRICES + what;
return mat4( texelFetch(matricesBuffer, i*4 + 0),
texelFetch(matricesBuffer, i*4 + 1),
texelFetch(matricesBuffer, i*4 + 2),
texelFetch(matricesBuffer, i*4 + 3));
}
#endif
================================================
FILE: csf.cpp
================================================
/*
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#define CSF_IMPLEMENTATION
#define CSF_SUPPORT_GLTF2 1
#define CSF_SUPPORT_FILEMAPPING 1
#include <fileformats/cadscenefile.h>
#define CGLTF_IMPLEMENTATION
#include <cgltf.h>
================================================
FILE: csfviewer.cpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#define DEBUG_FILTER 1
#include <nvgl/extensions_gl.hpp>
#include <imgui/backends/imgui_impl_gl.h>
#include <imgui/imgui_helper.h>
#include <nvgl/glsltypes_gl.hpp>
#include <nvh/cameracontrol.hpp>
#include <nvh/fileoperations.hpp>
#include <nvh/geometry.hpp>
#include <nvh/misc.hpp>
#include <nvgl/appwindowprofiler_gl.hpp>
#include <nvgl/base_gl.hpp>
#include <nvgl/error_gl.hpp>
#include <nvgl/programmanager_gl.hpp>
#include "transformsystem.hpp"
#include "cadscene.hpp"
#include "renderer.hpp"
#include <algorithm>
#include "common.h"
#include "glm/gtc/matrix_access.hpp"
#include "glm/gtc/type_ptr.hpp"
namespace csfviewer {
int const SAMPLE_SIZE_WIDTH(800);
int const SAMPLE_SIZE_HEIGHT(600);
int const SAMPLE_MAJOR_VERSION(4);
int const SAMPLE_MINOR_VERSION(5);
class Sample : public nvgl::AppWindowProfilerGL
{
public:
enum GuiEnums
{
GUI_RENDERER,
GUI_MSAA,
GUI_SHADE,
GUI_STRATEGY,
};
struct
{
nvgl::ProgramID draw_object, draw_object_tris, draw_object_line, draw_object_indexed, draw_object_indexed_tris,
draw_object_indexed_line,
cull_object_frustum, cull_object_hiz, cull_object_raster, cull_bit_temporallast, cull_bit_temporalnew,
cull_bit_regular, cull_depth_mips,
scan_prefixsum, scan_offsets, scan_combine,
transform_leaves, transform_level,
xplode;
} programs;
struct
{
GLuint scene = 0;
GLuint scene2 = 0;
} fbos;
struct
{
GLuint scene_ubo = 0;
} buffers;
struct
{
GLuint64 scene_ubo;
} addresses;
struct
{
GLuint scene_color = 0;
GLuint scene_color2 = 0;
GLuint scene_depthstencil = 0;
GLuint scene_depthstencil2 = 0;
} textures;
struct Tweak
{
int renderer = 0;
ShadeType shade = SHADE_SOLID;
Strategy strategy = STRATEGY_GROUPS;
int clones = 0;
bool cloneaxisX = true;
bool cloneaxisY = true;
bool cloneaxisZ = false;
bool animateActive = false;
float animateMin = 1;
float animateDelta = 1;
int zoom = 100;
int msaa = 0;
bool noUI = false;
};
nvgl::ProgramManager m_progManager;
ImGuiH::Registry m_ui;
double m_uiTime = 0;
Tweak m_tweak;
Tweak m_lastTweak;
std::string m_modelFilename;
SceneData m_sceneUbo;
CadScene m_scene;
TransformSystem m_transformSystem;
GLuint m_xplodeGroupSize;
std::vector<unsigned int> m_renderersSorted;
std::string m_rendererName;
Renderer* NV_RESTRICT m_renderer;
Resources m_resources;
size_t m_stateChangeID;
void updateProgramDefine();
bool initProgram();
bool initScene(const char* filename, int clones, int cloneaxis);
bool initFramebuffers(int width, int height);
void initRenderer(int type, Strategy strategy);
void deinitRenderer();
void getCullPrograms(CullingSystem::Programs& cullprograms);
void getScanPrograms(ScanSystem::Programs& scanprograms);
void getTransformPrograms(TransformSystem::Programs& xfromPrograms);
void updatedPrograms();
void setupConfigParameters();
void setRendererFromName();
public:
Sample() { setupConfigParameters(); }
bool validateConfig() override;
bool begin() override;
void think(double time) override;
void resize(int width, int height) override;
void processUI(double time);
nvh::CameraControl m_control;
void end() override { ImGui::ShutdownGL(); }
// return true to prevent m_windowState updates
bool mouse_pos(int x, int y) override
{
if(m_tweak.noUI)
return false;
return ImGuiH::mouse_pos(x, y);
}
bool mouse_button(int button, int action) override
{
if(m_tweak.noUI)
return false;
return ImGuiH::mouse_button(button, action);
}
bool mouse_wheel(int wheel) override
{
if(m_tweak.noUI)
return false;
return ImGuiH::mouse_wheel(wheel);
}
bool key_char(int button) override
{
if(m_tweak.noUI)
return false;
return ImGuiH::key_char(button);
}
bool key_button(int button, int action, int mods) override
{
if(m_tweak.noUI)
return false;
return ImGuiH::key_button(button, action, mods);
}
};
void Sample::updateProgramDefine() {}
void Sample::getTransformPrograms(TransformSystem::Programs& xformPrograms)
{
xformPrograms.transform_leaves = m_progManager.get(programs.transform_leaves);
xformPrograms.transform_level = m_progManager.get(programs.transform_level);
}
void Sample::getCullPrograms(CullingSystem::Programs& cullprograms)
{
cullprograms.bit_regular = m_progManager.get(programs.cull_bit_regular);
cullprograms.bit_temporallast = m_progManager.get(programs.cull_bit_temporallast);
cullprograms.bit_temporalnew = m_progManager.get(programs.cull_bit_temporalnew);
cullprograms.depth_mips = m_progManager.get(programs.cull_depth_mips);
cullprograms.object_frustum = m_progManager.get(programs.cull_object_frustum);
cullprograms.object_hiz = m_progManager.get(programs.cull_object_hiz);
cullprograms.object_raster = m_progManager.get(programs.cull_object_raster);
}
void Sample::getScanPrograms(ScanSystem::Programs& scanprograms)
{
scanprograms.prefixsum = m_progManager.get(programs.scan_prefixsum);
scanprograms.offsets = m_progManager.get(programs.scan_offsets);
scanprograms.combine = m_progManager.get(programs.scan_combine);
}
bool Sample::initProgram()
{
bool validated(true);
m_progManager.m_filetype = nvh::ShaderFileManager::FILETYPE_GLSL;
m_progManager.addDirectory(std::string("GLSL_" PROJECT_NAME));
m_progManager.addDirectory(exePath() + std::string(PROJECT_RELDIRECTORY));
m_progManager.registerInclude("common.h");
updateProgramDefine();
programs.draw_object =
m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "scene.vert.glsl"),
nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "scene.frag.glsl"));
programs.draw_object_tris = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define WIREMODE 0\n", "scene.vert.glsl"),
nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define WIREMODE 0\n", "scene.frag.glsl"));
programs.draw_object_line = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define WIREMODE 1\n", "scene.vert.glsl"),
nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define WIREMODE 1\n", "scene.frag.glsl"));
programs.draw_object_indexed = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define USE_INDEXING 1\n", "scene.vert.glsl"),
nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define USE_INDEXING 1\n", "scene.frag.glsl"));
programs.draw_object_indexed_tris = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define USE_INDEXING 1\n#define WIREMODE 0\n",
"scene.vert.glsl"),
nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define USE_INDEXING 1\n#define WIREMODE 0\n",
"scene.frag.glsl"));
programs.draw_object_indexed_line = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define USE_INDEXING 1\n#define WIREMODE 1\n",
"scene.vert.glsl"),
nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define USE_INDEXING 1\n#define WIREMODE 1\n",
"scene.frag.glsl"));
programs.cull_object_raster = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define DUALINDEX 1\n#define MATRICES 4\n",
"cull-raster.vert.glsl"),
nvgl::ProgramManager::Definition(GL_GEOMETRY_SHADER, "#define DUALINDEX 1\n#define MATRICES 4\n",
"cull-raster.geo.glsl"),
nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define DUALINDEX 1\n#define MATRICES 4\n",
"cull-raster.frag.glsl"));
programs.cull_object_frustum = m_progManager.createProgram(nvgl::ProgramManager::Definition(
GL_VERTEX_SHADER, "#define DUALINDEX 1\n#define MATRICES 4\n", "cull-xfb.vert.glsl"));
programs.cull_object_hiz = m_progManager.createProgram(nvgl::ProgramManager::Definition(
GL_VERTEX_SHADER, "#define DUALINDEX 1\n#define MATRICES 4\n#define OCCLUSION\n", "cull-xfb.vert.glsl"));
programs.cull_bit_regular = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define TEMPORAL 0\n", "cull-bitpack.vert.glsl"));
programs.cull_bit_temporallast = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define TEMPORAL TEMPORAL_LAST\n", "cull-bitpack.vert.glsl"));
programs.cull_bit_temporalnew = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "#define TEMPORAL TEMPORAL_NEW\n", "cull-bitpack.vert.glsl"));
programs.cull_depth_mips =
m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "cull-downsample.vert.glsl"),
nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "cull-downsample.frag.glsl"));
programs.scan_prefixsum = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_COMPUTE_SHADER, "#define TASK TASK_SUM\n", "scan.comp.glsl"));
programs.scan_offsets = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_COMPUTE_SHADER, "#define TASK TASK_OFFSETS\n", "scan.comp.glsl"));
programs.scan_combine = m_progManager.createProgram(
nvgl::ProgramManager::Definition(GL_COMPUTE_SHADER, "#define TASK TASK_COMBINE\n", "scan.comp.glsl"));
programs.transform_leaves =
m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_COMPUTE_SHADER, "transform-leaves.comp.glsl"));
programs.transform_level =
m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_COMPUTE_SHADER, "transform-level.comp.glsl"));
programs.xplode =
m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_COMPUTE_SHADER, "xplode-animation.comp.glsl"));
validated = m_progManager.areProgramsValid();
return validated;
}
bool Sample::initScene(const char* filename, int clones, int cloneaxis)
{
m_scene.unload();
if(buffers.scene_ubo && has_GL_NV_shader_buffer_load)
{
glMakeNamedBufferNonResidentNV(buffers.scene_ubo);
}
nvgl::newBuffer(buffers.scene_ubo);
glNamedBufferStorage(buffers.scene_ubo, sizeof(SceneData), NULL, GL_DYNAMIC_STORAGE_BIT);
if(has_GL_NV_shader_buffer_load)
{
glGetNamedBufferParameterui64vNV(buffers.scene_ubo, GL_BUFFER_GPU_ADDRESS_NV, &addresses.scene_ubo);
glMakeNamedBufferResidentNV(buffers.scene_ubo, GL_READ_ONLY);
}
m_resources.sceneUbo = buffers.scene_ubo;
m_resources.sceneAddr = addresses.scene_ubo;
m_resources.stateChangeID++;
bool status = m_scene.loadCSF(filename, clones, cloneaxis);
LOGI("\nscene %s\n", filename);
LOGI("geometries: %6d\n", (uint32_t)m_scene.m_geometry.size());
LOGI("materials: %6d\n", (uint32_t)m_scene.m_materials.size());
LOGI("nodes: %6d\n", (uint32_t)m_scene.m_matrices.size());
LOGI("objects: %6d\n", (uint32_t)m_scene.m_objects.size());
LOGI("\n");
return status;
}
bool Sample::initFramebuffers(int width, int height)
{
bool layered = true;
if(!fbos.scene || m_tweak.msaa != m_lastTweak.msaa)
{
nvgl::newFramebuffer(fbos.scene);
nvgl::newFramebuffer(fbos.scene2);
m_resources.fbo = fbos.scene;
m_resources.fbo2 = fbos.scene2;
m_resources.stateChangeID++;
}
if(layered)
{
if(has_GL_NV_bindless_texture && textures.scene_color)
{
glMakeTextureHandleNonResidentNV(glGetTextureHandleNV(textures.scene_color));
glMakeTextureHandleNonResidentNV(glGetTextureHandleNV(textures.scene_depthstencil));
}
nvgl::newTexture(textures.scene_color, m_tweak.msaa ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY);
nvgl::newTexture(textures.scene_depthstencil, m_tweak.msaa ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY);
if(m_tweak.msaa)
{
glTextureStorage3DMultisample(textures.scene_color, m_tweak.msaa, GL_RGBA8, width, height, 2, GL_TRUE);
glTextureStorage3DMultisample(textures.scene_depthstencil, m_tweak.msaa, GL_DEPTH24_STENCIL8, width, height, 2, GL_TRUE);
}
else
{
glTextureStorage3D(textures.scene_color, 1, GL_RGBA8, width, height, 2);
glTextureStorage3D(textures.scene_depthstencil, 1, GL_DEPTH24_STENCIL8, width, height, 2);
}
glNamedFramebufferTextureLayer(fbos.scene, GL_COLOR_ATTACHMENT0, textures.scene_color, 0, 0);
glNamedFramebufferTextureLayer(fbos.scene, GL_DEPTH_STENCIL_ATTACHMENT, textures.scene_depthstencil, 0, 0);
glNamedFramebufferTextureLayer(fbos.scene2, GL_COLOR_ATTACHMENT0, textures.scene_color, 0, 1);
glNamedFramebufferTextureLayer(fbos.scene2, GL_DEPTH_STENCIL_ATTACHMENT, textures.scene_depthstencil, 0, 1);
if(has_GL_NV_bindless_texture)
{
glMakeTextureHandleResidentNV(glGetTextureHandleNV(textures.scene_color));
glMakeTextureHandleResidentNV(glGetTextureHandleNV(textures.scene_depthstencil));
}
}
else
{
if(has_GL_NV_bindless_texture && textures.scene_color)
{
glMakeTextureHandleNonResidentNV(glGetTextureHandleNV(textures.scene_color));
glMakeTextureHandleNonResidentNV(glGetTextureHandleNV(textures.scene_depthstencil));
glMakeTextureHandleNonResidentNV(glGetTextureHandleNV(textures.scene_color2));
glMakeTextureHandleNonResidentNV(glGetTextureHandleNV(textures.scene_depthstencil2));
}
nvgl::newTexture(textures.scene_color, m_tweak.msaa ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
nvgl::newTexture(textures.scene_depthstencil, m_tweak.msaa ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
if(m_tweak.msaa)
{
glTextureStorage2DMultisample(textures.scene_color, 1, GL_RGBA8, width, height, GL_TRUE);
glTextureStorage2DMultisample(textures.scene_depthstencil, 1, GL_DEPTH24_STENCIL8, width, height, GL_TRUE);
}
else
{
glTextureStorage2D(textures.scene_color, 1, GL_RGBA8, width, height);
glTextureStorage2D(textures.scene_depthstencil, 1, GL_DEPTH24_STENCIL8, width, height);
}
glNamedFramebufferTexture(fbos.scene, GL_COLOR_ATTACHMENT0, textures.scene_color, 0);
glNamedFramebufferTexture(fbos.scene, GL_DEPTH_STENCIL_ATTACHMENT, textures.scene_depthstencil, 0);
nvgl::newTexture(textures.scene_color2, m_tweak.msaa ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
nvgl::newTexture(textures.scene_depthstencil2, m_tweak.msaa ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D);
if(m_tweak.msaa)
{
glTextureStorage2DMultisample(textures.scene_color2, 1, GL_RGBA8, width, height, GL_TRUE);
glTextureStorage2DMultisample(textures.scene_depthstencil2, 1, GL_DEPTH24_STENCIL8, width, height, GL_TRUE);
}
else
{
glTextureStorage2D(textures.scene_color2, 1, GL_RGBA8, width, height);
glTextureStorage2D(textures.scene_depthstencil2, 1, GL_DEPTH24_STENCIL8, width, height);
}
glNamedFramebufferTexture(fbos.scene2, GL_COLOR_ATTACHMENT0, textures.scene_color2, 0);
glNamedFramebufferTexture(fbos.scene2, GL_DEPTH_STENCIL_ATTACHMENT, textures.scene_depthstencil2, 0);
if(has_GL_NV_bindless_texture)
{
glMakeTextureHandleResidentNV(glGetTextureHandleNV(textures.scene_color));
glMakeTextureHandleResidentNV(glGetTextureHandleNV(textures.scene_depthstencil));
glMakeTextureHandleResidentNV(glGetTextureHandleNV(textures.scene_color2));
glMakeTextureHandleResidentNV(glGetTextureHandleNV(textures.scene_depthstencil2));
}
}
m_resources.fboTextureChangeID++;
return true;
}
void Sample::deinitRenderer()
{
if(m_renderer)
{
m_renderer->deinit();
delete m_renderer;
m_renderer = NULL;
}
}
void Sample::initRenderer(int type, Strategy strategy)
{
deinitRenderer();
Renderer::getRegistry()[m_renderersSorted[type]]->updatedPrograms(m_progManager);
m_renderer = Renderer::getRegistry()[m_renderersSorted[type]]->create();
m_renderer->m_strategy = strategy;
m_renderer->init(&m_scene, m_resources);
}
bool Sample::begin()
{
m_renderer = NULL;
m_stateChangeID = 0;
ImGuiH::Init(m_windowState.m_winSize[0], m_windowState.m_winSize[1], this);
ImGui::InitGL();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
#if defined(NDEBUG)
setVsync(false);
#endif
Renderer::s_bindless_ubo = !!m_contextWindow.extensionSupported("GL_NV_uniform_buffer_unified_memory");
LOGI("\nNV_uniform_buffer_unified_memory support: %s\n\n", Renderer::s_bindless_ubo ? "true" : "false");
bool validated(true);
GLuint defaultVAO;
glGenVertexArrays(1, &defaultVAO);
glBindVertexArray(defaultVAO);
validated = validated && initProgram();
validated = validated && initScene(m_modelFilename.c_str(), 0, 3);
validated = validated && initFramebuffers(m_windowState.m_winSize[0], m_windowState.m_winSize[1]);
const Renderer::Registry registry = Renderer::getRegistry();
for(size_t i = 0; i < registry.size(); i++)
{
if(registry[i]->isAvailable())
{
if(!registry[i]->loadPrograms(m_progManager))
{
LOGE("Failed to load resources for renderer %s\n", registry[i]->name());
return false;
}
uint sortkey = uint(i);
sortkey |= registry[i]->priority() << 16;
m_renderersSorted.push_back(sortkey);
}
}
std::sort(m_renderersSorted.begin(), m_renderersSorted.end());
for(size_t i = 0; i < m_renderersSorted.size(); i++)
{
m_renderersSorted[i] &= 0xFFFF;
m_ui.enumAdd(GUI_RENDERER, int(i), registry[m_renderersSorted[i]]->name());
}
{
m_ui.enumAdd(GUI_STRATEGY, STRATEGY_INDIVIDUAL, "drawcall individual");
m_ui.enumAdd(GUI_STRATEGY, STRATEGY_JOIN, "drawcall join");
m_ui.enumAdd(GUI_STRATEGY, STRATEGY_GROUPS, "material groups");
m_ui.enumAdd(GUI_SHADE, SHADE_SOLID, toString(SHADE_SOLID));
m_ui.enumAdd(GUI_SHADE, SHADE_SOLIDWIRE, toString(SHADE_SOLIDWIRE));
m_ui.enumAdd(GUI_SHADE, SHADE_SOLIDWIRE_SPLIT, "solid w edges (split test, only in sorted)");
m_ui.enumAdd(GUI_MSAA, 0, "none");
m_ui.enumAdd(GUI_MSAA, 2, "2x");
m_ui.enumAdd(GUI_MSAA, 4, "4x");
m_ui.enumAdd(GUI_MSAA, 8, "8x");
}
m_control.m_sceneOrbit = glm::vec3(m_scene.m_bbox.max + m_scene.m_bbox.min) * 0.5f;
m_control.m_sceneDimension = glm::length((m_scene.m_bbox.max - m_scene.m_bbox.min));
m_control.m_viewMatrix =
glm::lookAt(m_control.m_sceneOrbit - (-vec3(1, 1, 1) * m_control.m_sceneDimension * 0.5f * (float(m_tweak.zoom) / 100.0f)),
m_control.m_sceneOrbit, vec3(0, 1, 0));
m_sceneUbo.wLightPos = (m_scene.m_bbox.max + m_scene.m_bbox.min) * 0.5f + m_control.m_sceneDimension;
m_sceneUbo.wLightPos.w = 1.0;
updatedPrograms();
CullingSystem::Programs cullprogs;
getCullPrograms(cullprogs);
Renderer::s_cullsys.init(cullprogs, true);
ScanSystem::Programs scanprogs;
getScanPrograms(scanprogs);
Renderer::s_scansys.init(scanprogs);
//Renderer::s_scansys.test();
TransformSystem::Programs xformprogs;
getTransformPrograms(xformprogs);
m_transformSystem.init(xformprogs);
initRenderer(m_tweak.renderer, m_tweak.strategy);
return validated;
}
void Sample::processUI(double time)
{
int width = m_windowState.m_winSize[0];
int height = m_windowState.m_winSize[1];
// Update imgui configuration
auto& imgui_io = ImGui::GetIO();
imgui_io.DeltaTime = static_cast<float>(time - m_uiTime);
imgui_io.DisplaySize = ImVec2(static_cast<float>(width), static_cast<float>(height));
m_uiTime = time;
ImGui::NewFrame();
ImGui::SetNextWindowSize(ImGuiH::dpiScaled(350, 0), ImGuiCond_FirstUseEver);
if(ImGui::Begin("NVIDIA " PROJECT_NAME, nullptr))
{
m_ui.enumCombobox(GUI_RENDERER, "renderer", &m_tweak.renderer);
m_ui.enumCombobox(GUI_STRATEGY, "strategy", &m_tweak.strategy);
m_ui.enumCombobox(GUI_SHADE, "shademode", &m_tweak.shade);
ImGui::Checkbox("xplode via GPU", &m_tweak.animateActive);
ImGui::SliderFloat("xplode min", &m_tweak.animateMin, 0, 16.0f);
ImGui::SliderFloat("xplode delta", &m_tweak.animateDelta, 0, 16.0f);
ImGuiH::InputIntClamped("clones", &m_tweak.clones, 0, 255, 1, 10, ImGuiInputTextFlags_EnterReturnsTrue);
ImGui::Checkbox("clone X", &m_tweak.cloneaxisX);
ImGui::Checkbox("clone Y", &m_tweak.cloneaxisY);
ImGui::Checkbox("clone Z", &m_tweak.cloneaxisZ);
m_ui.enumCombobox(GUI_MSAA, "msaa", &m_tweak.msaa);
}
if(!m_tweak.cloneaxisX && !m_tweak.cloneaxisY && !m_tweak.cloneaxisZ)
{
m_tweak.cloneaxisX = true;
}
ImGui::End();
}
void Sample::updatedPrograms()
{
CullingSystem::Programs cullprogs;
getCullPrograms(cullprogs);
Renderer::s_cullsys.update(cullprogs, true);
ScanSystem::Programs scanprogs;
getScanPrograms(scanprogs);
Renderer::s_scansys.update(scanprogs);
TransformSystem::Programs xformprogs;
getTransformPrograms(xformprogs);
m_transformSystem.update(xformprogs);
m_resources.programUbo = m_progManager.get(programs.draw_object);
m_resources.programUboLine = m_progManager.get(programs.draw_object_line);
m_resources.programUboTris = m_progManager.get(programs.draw_object_tris);
m_resources.programIdx = m_progManager.get(programs.draw_object_indexed);
m_resources.programIdxLine = m_progManager.get(programs.draw_object_indexed_line);
m_resources.programIdxTris = m_progManager.get(programs.draw_object_indexed_tris);
GLuint groupsizes[3];
glGetProgramiv(m_progManager.get(programs.xplode), GL_COMPUTE_WORK_GROUP_SIZE, (GLint*)groupsizes);
m_xplodeGroupSize = groupsizes[0];
m_resources.stateChangeID++;
}
void Sample::think(double time)
{
NV_PROFILE_GL_SECTION("Frame");
processUI(time);
m_control.processActions({m_windowState.m_winSize[0], m_windowState.m_winSize[1]},
glm::vec2(m_windowState.m_mouseCurrent[0], m_windowState.m_mouseCurrent[1]),
m_windowState.m_mouseButtonFlags, m_windowState.m_mouseWheel);
if(m_windowState.onPress(KEY_R))
{
m_progManager.reloadPrograms();
Renderer::getRegistry()[m_tweak.renderer]->updatedPrograms(m_progManager);
updatedPrograms();
}
if(m_tweak.msaa != m_lastTweak.msaa)
{
initFramebuffers(m_windowState.m_winSize[0], m_windowState.m_winSize[1]);
}
if(m_tweak.clones != m_lastTweak.clones || m_tweak.cloneaxisX != m_lastTweak.cloneaxisX
|| m_tweak.cloneaxisY != m_lastTweak.cloneaxisY || m_tweak.cloneaxisZ != m_lastTweak.cloneaxisZ)
{
deinitRenderer();
initScene(m_modelFilename.c_str(), m_tweak.clones,
(int(m_tweak.cloneaxisX) << 0) | (int(m_tweak.cloneaxisY) << 1) | (int(m_tweak.cloneaxisZ) << 2));
}
if(m_tweak.renderer != m_lastTweak.renderer || m_tweak.strategy != m_lastTweak.strategy
|| m_tweak.cloneaxisX != m_lastTweak.cloneaxisX || m_tweak.cloneaxisY != m_lastTweak.cloneaxisY
|| m_tweak.cloneaxisZ != m_lastTweak.cloneaxisZ || m_tweak.clones != m_lastTweak.clones)
{
initRenderer(m_tweak.renderer, m_tweak.strategy);
}
if(!m_tweak.animateActive && m_lastTweak.animateActive)
{
m_scene.resetMatrices();
}
m_lastTweak = m_tweak;
int width = m_windowState.m_winSize[0];
int height = m_windowState.m_winSize[1];
{
// generic state setup
glViewport(0, 0, width, height);
if(m_tweak.shade == SHADE_SOLIDWIRE_SPLIT)
{
glBindFramebuffer(GL_FRAMEBUFFER, fbos.scene2);
glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
glBindFramebuffer(GL_FRAMEBUFFER, fbos.scene);
glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
m_sceneUbo.viewport = ivec2(width, height);
glm::mat4 projection = glm::perspectiveRH_ZO((45.f), float(width) / float(height),
m_control.m_sceneDimension * 0.001f, m_control.m_sceneDimension * 10.0f);
glm::mat4 view = m_control.m_viewMatrix;
m_sceneUbo.viewProjMatrix = projection * view;
m_sceneUbo.viewMatrix = view;
m_sceneUbo.viewMatrixIT = glm::transpose(glm::inverse(view));
m_sceneUbo.viewPos = glm::row(m_sceneUbo.viewMatrixIT, 3);
m_sceneUbo.viewDir = -glm::row(view,2);
m_sceneUbo.wLightPos = glm::row(m_sceneUbo.viewMatrixIT, 3);
m_sceneUbo.wLightPos.w = 1.0;
m_sceneUbo.tboMatrices = uvec2(m_scene.m_matricesTexGLADDR & 0xFFFFFFFF, m_scene.m_matricesTexGLADDR >> 32);
glNamedBufferSubData(buffers.scene_ubo, 0, sizeof(SceneData), &m_sceneUbo);
glDisable(GL_CULL_FACE);
}
if(m_tweak.animateActive)
{
{
NV_PROFILE_GL_SECTION("Xplode");
float speed = 0.5;
float scale = m_tweak.animateMin + (cosf(float(time) * speed) * 0.5f + 0.5f) * (m_tweak.animateDelta);
GLuint totalNodes = GLuint(m_scene.m_matrices.size());
GLuint groupsize = m_xplodeGroupSize;
glUseProgram(m_progManager.get(programs.xplode));
glUniform1f(0, scale);
glUniform1i(1, totalNodes);
nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_BUFFER, m_scene.m_matricesOrigTexGL);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_scene.m_matricesGL);
glDispatchCompute((totalNodes + groupsize - 1) / groupsize, 1, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_BUFFER, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
glUseProgram(0);
}
{
NV_PROFILE_GL_SECTION("Tree");
TransformSystem::Buffer ids;
TransformSystem::Buffer world;
TransformSystem::Buffer object;
ids.buffer = m_scene.m_parentIDsGL;
ids.offset = 0;
ids.size = sizeof(GLuint) * m_scene.m_matrices.size();
world.buffer = m_scene.m_matricesGL;
world.offset = 0;
world.size = sizeof(CadScene::MatrixNode) * m_scene.m_matrices.size();
object.buffer = m_scene.m_matricesGL;
object.offset = 0;
object.size = sizeof(CadScene::MatrixNode) * m_scene.m_matrices.size();
m_transformSystem.process(m_scene.m_nodeTree, ids, object, world);
}
}
{
NV_PROFILE_GL_SECTION("Render");
m_resources.cullView.viewPos = glm::value_ptr(m_sceneUbo.viewPos);
m_resources.cullView.viewDir = glm::value_ptr(m_sceneUbo.viewDir);
m_resources.cullView.viewProjMatrix = glm::value_ptr(m_sceneUbo.viewProjMatrix);
m_renderer->draw(m_tweak.shade, m_resources, m_profiler, m_progManager);
}
{
NV_PROFILE_GL_SECTION("Blit");
if(m_tweak.shade == SHADE_SOLIDWIRE_SPLIT)
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
int wh = width / 2;
int hh = height / 2;
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos.scene);
glBlitFramebuffer(0, 0, wh, hh, 0, 0, wh, hh, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBlitFramebuffer(wh, hh, width, height, wh, hh, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos.scene2);
glBlitFramebuffer(wh, 0, width, hh, wh, 0, width, hh, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBlitFramebuffer(0, hh, wh, height, 0, hh, wh, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
else
{
// blit to background
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos.scene);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
}
if(!m_tweak.noUI)
{
NV_PROFILE_GL_SECTION("GUI");
ImGui::Render();
ImGui::RenderDrawDataGL(ImGui::GetDrawData());
}
ImGui::EndFrame();
m_lastTweak = m_tweak;
}
void Sample::resize(int width, int height)
{
initFramebuffers(width, height);
}
void Sample::setRendererFromName()
{
if(!m_rendererName.empty())
{
const Renderer::Registry registry = Renderer::getRegistry();
for(size_t i = 0; i < m_renderersSorted.size(); i++)
{
if(strcmp(m_rendererName.c_str(), registry[m_renderersSorted[i]]->name()) == 0)
{
m_tweak.renderer = int(i);
}
}
}
}
static std::string addPath(std::string const& defaultPath, std::string const& filename)
{
if(
#ifdef _WIN32
filename.find(':') != std::string::npos
#else
!filename.empty() && filename[0] == '/'
#endif
)
{
return filename;
}
else
{
return defaultPath + "/" + filename;
}
}
static bool endsWith(std::string const& s, std::string const& end)
{
if(s.length() >= end.length())
{
return (0 == s.compare(s.length() - end.length(), end.length(), end));
}
else
{
return false;
}
}
void Sample::setupConfigParameters()
{
m_parameterList.addFilename(".csf", &m_modelFilename);
m_parameterList.addFilename(".csf.gz", &m_modelFilename);
m_parameterList.addFilename(".gltf", &m_modelFilename);
m_parameterList.add("noui", &m_tweak.noUI, false);
m_parameterList.add("renderer", (uint32_t*)&m_tweak.renderer);
m_parameterList.add("renderernamed", &m_rendererName);
m_parameterList.add("strategy", (uint32_t*)&m_tweak.strategy);
m_parameterList.add("shademode", (uint32_t*)&m_tweak.shade);
m_parameterList.add("msaa", &m_tweak.msaa);
m_parameterList.add("clones", &m_tweak.clones);
m_parameterList.add("xplode", &m_tweak.animateActive);
m_parameterList.add("zoom", &m_tweak.zoom);
}
bool Sample::validateConfig()
{
if(m_modelFilename.empty())
{
LOGI("no .csf model file specified\n");
LOGI("exe <filename.csf/cfg> parameters...\n");
m_parameterList.print();
return false;
}
return true;
}
} // namespace csfviewer
using namespace csfviewer;
int main(int argc, const char** argv)
{
NVPSystem system(PROJECT_NAME);
Sample sample;
{
std::vector<std::string> directories;
directories.push_back(NVPSystem::exePath());
directories.push_back(NVPSystem::exePath() + "/media");
directories.push_back(NVPSystem::exePath() + std::string(PROJECT_DOWNLOAD_RELDIRECTORY));
sample.m_modelFilename = nvh::findFile(std::string("geforce.csf.gz"), directories);
}
return sample.run(PROJECT_NAME, argc, argv, SAMPLE_SIZE_WIDTH, SAMPLE_SIZE_HEIGHT);
}
================================================
FILE: cull-bitpack.vert.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 330
/**/
#define TEMPORAL_LAST 1
#define TEMPORAL_NEW 2
#ifndef TEMPORAL
#define TEMPORAL 0
#endif
#extension GL_ARB_explicit_attrib_location : require
#extension GL_ARB_shader_storage_buffer_object : enable
layout(location=0) in uvec4 instream[8];
#if TEMPORAL
layout(location=9) in uint last;
#endif
#if GL_ARB_shader_storage_buffer_object
layout(std430,binding=0) writeonly buffer outputBuffer {
uint outstream[];
};
void storeOutput(uint value)
{
outstream[gl_VertexID] = value;
}
#else
flat out uint outstream;
void storeOutput(uint value)
{
outstream= value;
}
#endif
void main ()
{
uint bits = 0u;
int outbit = 0;
for (int i = 0; i < 8; i++){
for (int n = 0; n < 4; n++, outbit++){
uint checkbytes = instream[i][n];
bits |= (checkbytes & 1u) << outbit;
}
}
#if TEMPORAL == TEMPORAL_LAST
// render what was visible in last frame and passes current test
bits &= last;
#elif TEMPORAL == TEMPORAL_NEW
// render what was not visible in last frame (already rendered), but is now visible
bits &= (~last);
#endif
storeOutput(bits);
}
================================================
FILE: cull-downsample.frag.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 330
/**/
uniform sampler2D depthTex;
uniform int depthLod;
uniform bool evenLod;
in vec2 uv;
void main()
{
ivec2 lodSize = textureSize(depthTex,depthLod);
float depth = 0;
if (evenLod){
ivec2 offsets[] = ivec2[](
ivec2(0,0),
ivec2(0,1),
ivec2(1,1),
ivec2(1,0)
);
ivec2 coord = ivec2(gl_FragCoord.xy);
coord *= 2;
for (int i = 0; i < 4; i++){
depth = max(
depth,
texelFetch(depthTex,
clamp(coord + offsets[i], ivec2(0), lodSize - ivec2(1)),
depthLod).r );
}
}
else{
// need this to handle non-power of two
// very conservative
vec2 offsets[] = vec2[](
vec2(-1,-1),
vec2( 0,-1),
vec2( 1,-1),
vec2(-1, 0),
vec2( 0, 0),
vec2( 1, 0),
vec2(-1, 1),
vec2( 0, 1),
vec2( 1, 1)
);
vec2 coord = uv;
vec2 texel = 1.0/(vec2(lodSize));
for (int i = 0; i < 9; i++){
vec2 pos = coord + offsets[i] * texel;
depth = max(
depth,
#if 1
texelFetch(depthTex,
clamp(ivec2(pos * lodSize), ivec2(0), lodSize - ivec2(1)),
depthLod).r
#else
textureLod(depthTex,
pos,
depthLod).r
#endif
);
}
}
gl_FragDepth = depth;
}
================================================
FILE: cull-downsample.vert.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 330
/**/
out vec2 uv;
void main()
{
vec4 pos = vec4(
(float( gl_VertexID &1)) * 4.0 - 1.0,
(float((gl_VertexID>>1)&1)) * 4.0 - 1.0,
0, 1.0);
uv = pos.xy * 0.5 + 0.5;
gl_Position = pos;
}
================================================
FILE: cull-raster.frag.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 430
/**/
layout(early_fragment_tests) in;
layout(std430,binding=0) buffer visibleBuffer {
int visibles[];
};
layout(location=0,index=0) out vec4 out_Color;
flat in int objid;
void main (){
visibles[objid] = 1;
out_Color = unpackUnorm4x8(uint(objid));
}
================================================
FILE: cull-raster.geo.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 430
/**/
#ifndef MATRIX_WORLD
#define MATRIX_WORLD 0
#endif
#ifndef MATRIX_WORLD_IT
#define MATRIX_WORLD_IT 1
#endif
#ifndef MATRICES
#define MATRICES 2
#endif
#ifndef FLIPWIND
#define FLIPWIND 1
#endif
#ifndef PERSPECTIVE
#define PERSPECTIVE 1
#endif
// render the 3 visible sides based on view direction and box normal
layout(points,invocations=3) in;
// one side each invocation
layout(triangle_strip,max_vertices=4) out;
in VertexOut{
vec3 bboxCtr;
vec3 bboxDim;
flat int matrixIndex;
flat int objid;
} IN[1];
flat out int objid;
uniform vec3 viewPos;
uniform vec3 viewDir;
uniform mat4 viewProjTM;
uniform samplerBuffer matricesTex;
void main()
{
int matindex = (IN[0].matrixIndex*MATRICES + MATRIX_WORLD)*4;
mat4 worldTM = mat4(
texelFetch(matricesTex,matindex + 0),
texelFetch(matricesTex,matindex + 1),
texelFetch(matricesTex,matindex + 2),
texelFetch(matricesTex,matindex + 3));
vec3 faceNormal = vec3(0);
vec3 edgeBasis0 = vec3(0);
vec3 edgeBasis1 = vec3(0);
int id = gl_InvocationID;
if (id == 0)
{
faceNormal.x = IN[0].bboxDim.x;
edgeBasis0.y = IN[0].bboxDim.y;
edgeBasis1.z = IN[0].bboxDim.z;
}
else if(id == 1)
{
faceNormal.y = IN[0].bboxDim.y;
edgeBasis1.x = IN[0].bboxDim.x;
edgeBasis0.z = IN[0].bboxDim.z;
}
else if(id == 2)
{
faceNormal.z = IN[0].bboxDim.z;
edgeBasis0.x = IN[0].bboxDim.x;
edgeBasis1.y = IN[0].bboxDim.y;
}
vec3 worldCtr = (worldTM * vec4(IN[0].bboxCtr, 1)).xyz;
#if PERSPECTIVE
vec3 worldNormal = mat3(worldTM) * faceNormal;
vec3 worldPos = worldCtr + worldNormal;
float proj = sign(dot(worldPos - viewPos.xyz, worldNormal));
#else
vec3 worldNormal = mat3(worldTM) * faceNormal;
float proj = sign(dot(viewDir,worldNormal));
#endif
#if FLIPWIND
proj *= -1;
#endif
faceNormal = mat3(worldTM) * (faceNormal) * proj;
edgeBasis0 = mat3(worldTM) * (edgeBasis0);
edgeBasis1 = mat3(worldTM) * (edgeBasis1) * proj;
#if FLIPWIND
objid = IN[0].objid;
gl_Position = viewProjTM * vec4(worldCtr + (faceNormal - edgeBasis0 - edgeBasis1),1);
EmitVertex();
objid = IN[0].objid;
gl_Position = viewProjTM * vec4(worldCtr + (faceNormal + edgeBasis0 - edgeBasis1),1);
EmitVertex();
objid = IN[0].objid;
gl_Position = viewProjTM * vec4(worldCtr + (faceNormal - edgeBasis0 + edgeBasis1),1);
EmitVertex();
objid = IN[0].objid;
gl_Position = viewProjTM * vec4(worldCtr + (faceNormal + edgeBasis0 + edgeBasis1),1);
EmitVertex();
#else
objid = IN[0].objid;
gl_Position = viewProjTM * vec4(worldCtr + (faceNormal - edgeBasis0 - edgeBasis1),1);
EmitVertex();
objid = IN[0].objid;
gl_Position = viewProjTM * vec4(worldCtr + (faceNormal - edgeBasis0 + edgeBasis1),1);
EmitVertex();
objid = IN[0].objid;
gl_Position = viewProjTM * vec4(worldCtr + (faceNormal + edgeBasis0 - edgeBasis1),1);
EmitVertex();
objid = IN[0].objid;
gl_Position = viewProjTM * vec4(worldCtr + (faceNormal + edgeBasis0 + edgeBasis1),1);
EmitVertex();
#endif
}
================================================
FILE: cull-raster.vert.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 430
/**/
#ifndef MATRIX_WORLD
#define MATRIX_WORLD 0
#endif
#ifndef MATRIX_WORLD_IT
#define MATRIX_WORLD_IT 1
#endif
#ifndef MATRICES
#define MATRICES 2
#endif
layout(std430,binding=0) buffer visibleBuffer {
int visibles[];
};
uniform samplerBuffer matricesTex;
#ifdef DUALINDEX
layout(location=0) in int bboxIndex;
layout(location=2) in int matrixIndex;
uniform samplerBuffer bboxesTex;
vec4 bboxMin = texelFetch(bboxesTex, bboxIndex*2+0);
vec4 bboxMax = texelFetch(bboxesTex, bboxIndex*2+1);
#else
layout(location=0) in vec4 bboxMin;
layout(location=1) in vec4 bboxMax;
layout(location=2) in int matrixIndex;
#endif
uniform vec3 viewPos;
out VertexOut{
vec3 bboxCtr;
vec3 bboxDim;
flat int matrixIndex;
flat int objid;
} OUT;
void main()
{
int objid = gl_VertexID;
vec3 ctr =((bboxMin + bboxMax)*0.5).xyz;
vec3 dim =((bboxMax - bboxMin)*0.5).xyz;
OUT.bboxCtr = ctr;
OUT.bboxDim = dim;
OUT.matrixIndex = matrixIndex;
OUT.objid = objid;
{
// if camera is inside the bbox then none of our
// side faces will be visible, must treat object as
// visible
int matindex = (matrixIndex * MATRICES + MATRIX_WORLD_IT)*4;
mat4 worldInvTransTM = mat4(
texelFetch(matricesTex,matindex + 0),
texelFetch(matricesTex,matindex + 1),
texelFetch(matricesTex,matindex + 2),
texelFetch(matricesTex,matindex + 3));
vec3 objPos = (vec4(viewPos,1) * worldInvTransTM).xyz;
objPos -= ctr;
if (all(lessThan(abs(objPos),dim))){
// inside bbox
visibles[objid] = 1;
}
}
}
================================================
FILE: cull-tokencmds.vert.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 440
/**/
#define SCAN_BATCHSIZE 2048
layout(location=0) in uint cmdOffset;
layout(location=1) in uint cmdCullSize;
layout(location=2) in uint cmdCullScan;
uniform uint startOffset;
uniform int startID;
uniform uint endOffset;
uniform int endID;
uniform uint terminateCmd;
layout(std430,binding=0) writeonly buffer outputBuffer {
uint outcmds[];
};
layout(std430,binding=1) readonly buffer commandBuffer {
uint incmds[];
};
layout(std430,binding=2) readonly buffer cullSizesBuffer {
uint cullSizes[];
};
layout(std430,binding=3) readonly buffer cullScanBuffer {
uint cullScan[];
};
layout(std430,binding=4) readonly buffer cullScanOffsetBuffer {
uint cullScanOffsets[];
};
uint getOffset( int id, uint scan, uint size, bool exclusive)
{
int scanBatch = id / SCAN_BATCHSIZE;
uint scanOffset = scan;
scanOffset += scanBatch > 0 ? cullScanOffsets[ scanBatch-1] : 0;
if (exclusive){
scanOffset -= size;
}
return scanOffset;
}
uint getOffset( int id, bool exclusive)
{
return getOffset(id, cullScan[id], cullSizes[id], exclusive);
}
uint rebaseOffset(uint cullOffset)
{
// where the current sequence starts
uint startCullOffset = getOffset(startID, true);
// rebase from where it should start
uint outOffset = startOffset + (cullOffset - startCullOffset);
return outOffset;
}
#define DEBUG 0
void main ()
{
if (cmdCullSize > 0)
{
// cullOffset goes across "stateobject" sequences
uint cullOffset = getOffset(gl_VertexID,cmdCullScan,cmdCullSize,true);
uint outOffset = rebaseOffset(cullOffset);
#if DEBUG
outcmds[(gl_VertexID)*2+0] = outOffset;
outcmds[(gl_VertexID)*2+1] = cmdOffset;
#else
for (uint i = 0; i < cmdCullSize; i++){
outcmds[outOffset+i] = incmds[cmdOffset+i];
}
#endif
}
#if DEBUG
else {
outcmds[(gl_VertexID)*2+0] = ~0;
outcmds[(gl_VertexID)*2+1] = cmdOffset;
}
#endif
if (gl_VertexID == startID)
{
// add terminator if sequence not original
uint lastOffset = rebaseOffset( getOffset(endID, false) );
if (lastOffset != endOffset) {
#if !DEBUG
outcmds[lastOffset] = terminateCmd;
#endif
}
#if DEBUG && 0
outcmds[(startID)*2+0] = lastOffset;
outcmds[(startID)*2+1] = endOffset;
#endif
}
}
================================================
FILE: cull-tokensizes.vert.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 440
/**/
layout(location=0) in uint cmdSize;
layout(location=1) in int cmdObject;
layout(std430,binding=0) writeonly buffer outputBuffer {
uint outsizes[];
};
layout(std430,binding=1) readonly buffer visibleBuffer {
int visibles[];
};
#define DEBUG false
void main ()
{
if (cmdObject >= 0 && !DEBUG){
outsizes[gl_VertexID] = (visibles[cmdObject/32] & (1<<(cmdObject%32))) != 0 ? cmdSize : 0;
}
else{
outsizes[gl_VertexID] = cmdSize;
}
}
================================================
FILE: cull-xfb.vert.glsl
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#version 330
/**/
#ifndef MATRIX_WORLD
#define MATRIX_WORLD 0
#endif
#ifndef MATRIX_WORLD_IT
#define MATRIX_WORLD_IT 1
#endif
#ifndef MATRICES
#define MATRICES 2
#endif
#extension GL_ARB_explicit_attrib_location : require
#extension GL_ARB_shader_storage_buffer_object : enable
//#define OCCLUSION
#ifdef DUALINDEX
layout(location=0) in int bboxIndex;
layout(location=2) in int matrixIndex;
uniform samplerBuffer bboxesTex;
vec4 bboxMin = texelFetch(bboxesTex, bboxIndex*2+0);
vec4 bboxMax = texelFetch(bboxesTex, bboxIndex*2+1);
#else
layout(location=0) in vec4 bboxMin;
layout(location=1) in vec4 bboxMax;
layout(location=2) in int matrixIndex;
#endif
#if GL_ARB_shader_storage_buffer_object
layout(std430,binding=0) writeonly buffer outputBuffer {
int outstream[];
};
void storeOutput(int value)
{
outstream[gl_VertexID] = value;
}
#else
flat out int outstream;
void storeOutput(int value)
{
outstream = value;
}
#endif
uniform mat4 viewProjTM;
uniform samplerBuffer matricesTex;
#ifdef OCCLUSION
uniform sampler2D depthTex;
#endif
vec4 getBoxCorner(int n)
{
#if 1
bvec3 useMax = bvec3((n & 1) != 0, (n & 2) != 0, (n & 4) != 0);
return vec4(mix(bboxMin.xyz, bboxMax.xyz, useMax),1);
#else
switch(n){
case 0:
return vec4(bboxMin.x,bboxMin.y,bboxMin.z,1);
case 1:
return vec4(bboxMax.x,bboxMin.y,bboxMin.z,1);
case 2:
return vec4(bboxMin.x,bboxMax.y,bboxMin.z,1);
case 3:
return vec4(bboxMax.x,bboxMax.y,bboxMin.z,1);
case 4:
return vec4(bboxMin.x,bboxMin.y,bboxMax.z,1);
case 5:
return vec4(bboxMax.x,bboxMin.y,bboxMax.z,1);
case 6:
return vec4(bboxMin.x,bboxMax.y,bboxMax.z,1);
case 7:
return vec4(bboxMax.x,bboxMax.y,bboxMax.z,1);
}
#endif
}
vec3 projected(mat4 a, vec4 pos) {
vec4 hpos = (a * pos);
return hpos.xyz/hpos.w;
}
void main (){
int isvisible = 0;
int matindex = (matrixIndex*MATRICES + MATRIX_WORLD)*4;
mat4 worldTM = mat4(
texelFetch(matricesTex,matindex + 0),
texelFetch(matricesTex,matindex + 1),
texelFetch(matricesTex,matindex + 2),
texelFetch(matricesTex,matindex + 3));
mat4 worldViewProjTM = (viewProjTM * worldTM);
// clipspace bbox
vec3 clipmin = projected(worldViewProjTM, getBoxCorner(0));
vec3 clipmax = clipmin;
for (int n = 1; n < 8; n++){
vec3 ab = projected(worldViewProjTM, getBoxCorner(n));
clipmin = min(clipmin,ab);
clipmax = max(clipmax,ab);
}
isvisible = (
clipmin.x <= 1 &&
clipmin.y <= 1 &&
clipmin.z <= 1 &&
clipmax.x >= -1 &&
clipmax.y >= -1 &&
clipmax.z >= -1) ? 1 : 0;
#ifdef OCCLUSION
if (isvisible != 0){
clipmin = clipmin * 0.5 + 0.5;
clipmax = clipmax * 0.5 + 0.5;
vec2 size = (clipmax.xy - clipmin.xy);
ivec2 texsize = textureSize(depthTex,0);
float maxsize = max(size.x, size.y) * float(max(texsize.x,texsize.y));
float miplevel = ceil(log2(maxsize));
float depth = 0;
float a = textureLod(depthTex,clipmin.xy,miplevel).r;
float b = textureLod(depthTex,vec2(clipmax.x,clipmin.y),miplevel).r;
float c = textureLod(depthTex,clipmax.xy,miplevel).r;
float d = textureLod(depthTex,vec2(clipmin.x,clipmax.y),miplevel).r;
depth = max(depth,max(max(max(a,b),c),d));
isvisible = clipmin.z <= depth ? 1 : 0;
}
#endif
storeOutput(isvisible);
}
================================================
FILE: cullingsystem.cpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#include "cullingsystem.hpp"
#include <assert.h>
#include <string.h>
#define DEBUG_VISIBLEBOXES 0
inline unsigned int minDivide(unsigned int val, unsigned int alignment)
{
return (val+alignment-1)/alignment;
}
void CullingSystem::init( const Programs &programs, bool dualindex )
{
update(programs,dualindex);
glGenFramebuffers(1,&m_fbo);
glCreateTextures(GL_TEXTURE_BUFFER,2,m_tbo);
}
void CullingSystem::update( const Programs &programs, bool dualindex )
{
m_programs = programs;
m_dualindex = dualindex;
m_useSSBO = has_GL_VERSION_4_2 != 0;
m_useRepesentativeTest = !!has_GL_NV_representative_fragment_test;
if (!m_useSSBO)
{
const char* xfbstreams[] = {"outstream"};
glTransformFeedbackVaryings(programs.bit_regular,1,xfbstreams,GL_INTERLEAVED_ATTRIBS);
glLinkProgram(programs.bit_regular);
glTransformFeedbackVaryings(programs.bit_temporallast,1,xfbstreams,GL_INTERLEAVED_ATTRIBS);
glLinkProgram(programs.bit_temporallast);
glTransformFeedbackVaryings(programs.bit_temporalnew,1,xfbstreams,GL_INTERLEAVED_ATTRIBS);
glLinkProgram(programs.bit_temporalnew);
glTransformFeedbackVaryings(programs.object_frustum,1,xfbstreams,GL_INTERLEAVED_ATTRIBS);
glLinkProgram(programs.object_frustum);
glTransformFeedbackVaryings(programs.object_hiz,1,xfbstreams,GL_INTERLEAVED_ATTRIBS);
glLinkProgram(programs.object_hiz);
}
glUseProgram(programs.depth_mips);
glUniform1i(glGetUniformLocation(programs.depth_mips,"depthTex"),0);
m_uniforms.depth_lod = glGetUniformLocation(programs.depth_mips,"depthLod");
m_uniforms.depth_even = glGetUniformLocation(programs.depth_mips,"evenLod");
glUseProgram(programs.object_frustum);
glUniform1i(glGetUniformLocation(programs.object_frustum,"matricesTex"),0);
if (dualindex){
glUniform1i(glGetUniformLocation(programs.object_frustum,"bboxesTex"),1);
}
m_uniforms.frustum_viewProj = glGetUniformLocation(programs.object_frustum, "viewProjTM");
glUseProgram(programs.object_hiz);
glUniform1i(glGetUniformLocation(programs.object_hiz,"matricesTex"),0);
if (dualindex){
glUniform1i(glGetUniformLocation(programs.object_frustum,"bboxesTex"),1);
}
glUniform1i(glGetUniformLocation(programs.object_hiz,"depthTex"),2);
m_uniforms.hiz_viewProj = glGetUniformLocation(programs.object_hiz, "viewProjTM");
glUseProgram(programs.object_raster);
glUniform1i(glGetUniformLocation(programs.object_raster,"matricesTex"),0);
if (dualindex){
glUniform1i(glGetUniformLocation(programs.object_frustum,"bboxesTex"),1);
}
m_uniforms.raster_viewProj = glGetUniformLocation(programs.object_raster, "viewProjTM");
m_uniforms.raster_viewPos = glGetUniformLocation(programs.object_raster, "viewPos");
m_uniforms.raster_viewDir = glGetUniformLocation(programs.object_raster, "viewDir");
glUseProgram(0);
}
void CullingSystem::deinit()
{
glDeleteFramebuffers(1,&m_fbo);
glDeleteTextures(2,m_tbo);
}
void CullingSystem::buildDepthMipmaps( GLuint textureDepth, int width, int height )
{
int level = 0;
int dim = width > height ? width : height;
int twidth = width;
int theight = height;
int wasEven = 0;
glBindFramebuffer(GL_FRAMEBUFFER,m_fbo);
glDepthFunc(GL_ALWAYS);
glUseProgram(m_programs.depth_mips);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textureDepth);
while (dim){
if (level){
twidth = twidth < 1 ? 1 : twidth;
theight = theight < 1 ? 1 : theight;
glViewport(0,0,twidth,theight);
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_STENCIL_ATTACHMENT,GL_TEXTURE_2D, textureDepth, level);
glUniform1i(m_uniforms.depth_lod, level-1);
glUniform1i(m_uniforms.depth_even, wasEven);
glDrawArrays(GL_TRIANGLES,0,3);
}
wasEven = (twidth % 2 == 0) && (theight % 2 == 0);
dim /= 2;
twidth /= 2;
theight /= 2;
level++;
}
glUseProgram(0);
glViewport(0,0,width,height);
glBindFramebuffer(GL_FRAMEBUFFER,0);
glBindTexture(GL_TEXTURE_2D, 0);
glDepthFunc(GL_LEQUAL);
glViewport(0,0,width,height);
}
void CullingSystem::testBboxes( Job &job, bool raster )
{
// send the scene's bboxes as points stream
glBindBuffer(GL_ARRAY_BUFFER, job.m_bufferObjectBbox.buffer);
if (m_dualindex){
glVertexAttribIPointer(0, 1, GL_INT, job.m_bufferObjectBbox.stride, (const void*) job.m_bufferObjectBbox.offset);
glVertexAttribDivisor(0, 0);
glEnableVertexAttribArray(0);
}
else{
GLsizei stride = job.m_bufferObjectBbox.stride ? job.m_bufferObjectBbox.stride : sizeof(float)*4*2;
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, stride, (const void*)job.m_bufferObjectBbox.offset);
glVertexAttribDivisor(0, 0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, stride, (const void*)(sizeof(float)*4 + job.m_bufferObjectBbox.offset));
glVertexAttribDivisor(1, 0);
glEnableVertexAttribArray(1);
}
glBindBuffer(GL_ARRAY_BUFFER, job.m_bufferObjectMatrix.buffer);
glVertexAttribIPointer(2, 1, GL_INT, job.m_bufferObjectMatrix.stride, (const void*) job.m_bufferObjectMatrix.offset);
glVertexAttribDivisor(2, 0);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_BUFFER, m_tbo[0]);
job.m_bufferMatrices.TexBuffer(GL_TEXTURE_BUFFER,GL_RGBA32F);
if (m_dualindex){
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, m_tbo[1]);
job.m_bufferBboxes.TexBuffer(GL_TEXTURE_BUFFER,GL_RGBA32F);
}
if (raster){
if (m_useRepesentativeTest) {
glEnable( GL_REPRESENTATIVE_FRAGMENT_TEST_NV );
}
#if !DEBUG_VISIBLEBOXES
glDepthMask(GL_FALSE);
glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
#endif
}
else if (m_useSSBO){
glEnable(GL_RASTERIZER_DISCARD);
job.m_bufferVisOutput.BindBufferRange(GL_SHADER_STORAGE_BUFFER,0);
}
else{
glEnable(GL_RASTERIZER_DISCARD);
// setup transform feedback
job.m_bufferVisOutput.BindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER,0);
glBeginTransformFeedback(GL_POINTS);
}
glDrawArrays(GL_POINTS,0,job.m_numObjects);
if (raster){
if (m_useRepesentativeTest) {
glDisable( GL_REPRESENTATIVE_FRAGMENT_TEST_NV );
}
#if !DEBUG_VISIBLEBOXES
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
#endif
}
else if (m_useSSBO){
glBindBufferBase(GL_SHADER_STORAGE_BUFFER,0,0);
glDisable(GL_RASTERIZER_DISCARD);
}
else{
glEndTransformFeedback();
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER,0,0);
glDisable(GL_RASTERIZER_DISCARD);
}
if (m_dualindex){
glBindTexture(GL_TEXTURE_BUFFER, 0);
glActiveTexture(GL_TEXTURE0);
}
glBindTexture(GL_TEXTURE_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
void CullingSystem::bitsFromOutput( Job &job, BitType type)
{
// for GL 3.3 compatibility we use xfb
// in GL 4.3 SSBO is used
//
// using compute instead of "invisible" point drawing
// would be better if we had really huge thread counts
glEnable(GL_RASTERIZER_DISCARD);
glBindBuffer(GL_ARRAY_BUFFER, job.m_bufferVisOutput.buffer);
for (int i = 0; i < 8; i++){
glVertexAttribIPointer(i, 4, GL_UNSIGNED_INT, sizeof(int)*32, (const void*)(i*sizeof(int)*4 + job.m_bufferVisOutput.offset));
glVertexAttribDivisor(i, 0);
glEnableVertexAttribArray(i);
}
if (type == BITS_CURRENT){
glUseProgram(m_programs.bit_regular);
}
else{
glUseProgram(type == BITS_CURRENT_AND_LAST ? m_programs.bit_temporallast : m_programs.bit_temporalnew);
glBindBuffer(GL_ARRAY_BUFFER, job.m_bufferVisBitsLast.buffer);
glVertexAttribIPointer(9, 1, GL_UNSIGNED_INT, sizeof(int), (const void*)job.m_bufferVisBitsLast.offset);
glEnableVertexAttribArray(9);
}
if (m_useSSBO){
job.m_bufferVisBitsCurrent.BindBufferRange(GL_SHADER_STORAGE_BUFFER,0);
glMemoryBarrier(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT);
}
else{
job.m_bufferVisBitsCurrent.BindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER,0);
glBeginTransformFeedback(GL_POINTS);
}
glDrawArrays(GL_POINTS,0, minDivide(job.m_numObjects,32));
if (m_useSSBO){
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0);
}
else{
glEndTransformFeedback();
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
}
glDisableVertexAttribArray(9);
for (int i = 0; i < 8; i++){
glDisableVertexAttribArray(i);
}
glDisable(GL_RASTERIZER_DISCARD);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void CullingSystem::resultFromBits( Job &job )
{
job.resultFromBits(job.m_bufferVisBitsCurrent);
}
void CullingSystem::resultClient(Job &job)
{
job.resultClient();
}
void CullingSystem::buildOutput( MethodType method, Job &job, const View& view )
{
switch(method){
case METHOD_FRUSTUM:
{
glUseProgram(m_programs.object_frustum);
glUniformMatrix4fv(m_uniforms.frustum_viewProj, 1 ,GL_FALSE, view.viewProjMatrix);
testBboxes(job,false);
}
break;
case METHOD_HIZ:
{
glUseProgram(m_programs.object_hiz);
glUniformMatrix4fv(m_uniforms.hiz_viewProj, 1, GL_FALSE, view.viewProjMatrix);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D,job.m_textureDepthWithMipmaps);
testBboxes(job,false);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D,0);
glActiveTexture(GL_TEXTURE0);
}
break;
case METHOD_RASTER:
{
// clear visibles
job.m_bufferVisOutput.BindBufferRange(GL_SHADER_STORAGE_BUFFER,0);
glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R32UI,GL_RED_INTEGER,GL_UNSIGNED_INT,0);
glUseProgram(m_programs.object_raster);
glUniformMatrix4fv(m_uniforms.raster_viewProj, 1, GL_FALSE, view.viewProjMatrix);
glUniform3fv(m_uniforms.raster_viewPos, 1, view.viewPos);
glUniform3fv(m_uniforms.raster_viewDir, 1, view.viewDir);
glEnable( GL_POLYGON_OFFSET_FILL );
glPolygonOffset(-1,-1);
testBboxes(job,true);
glPolygonOffset(0,0);
glDisable( GL_POLYGON_OFFSET_FILL );
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glBindBufferBase (GL_SHADER_STORAGE_BUFFER,0,0);
}
break;
}
}
void CullingSystem::swapBits( Job &job )
{
Buffer temp = job.m_bufferVisBitsCurrent;
job.m_bufferVisBitsCurrent = job.m_bufferVisBitsLast;
job.m_bufferVisBitsLast = temp;
}
void CullingSystem::JobIndirectUnordered::resultFromBits( const Buffer& bufferVisBitsCurrent )
{
glEnable(GL_RASTERIZER_DISCARD);
glUseProgram(m_program_indirect_compact);
m_bufferIndirectCounter.BindBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0);
m_bufferIndirectCounter.ClearBufferSubData (GL_ATOMIC_COUNTER_BUFFER, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
bufferVisBitsCurrent. BindBufferRange(GL_SHADER_STORAGE_BUFFER, 2);
m_bufferObjectIndirects.BindBufferRange(GL_SHADER_STORAGE_BUFFER, 1);
m_bufferIndirectResult. BindBufferRange(GL_SHADER_STORAGE_BUFFER, 0);
m_bufferIndirectResult. ClearBufferSubData(GL_SHADER_STORAGE_BUFFER, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glDrawArrays(GL_POINTS,0,m_numObjects);
glDisable(GL_RASTERIZER_DISCARD);
glBindBufferBase (GL_ATOMIC_COUNTER_BUFFER, 0, 0);
glBindBufferBase (GL_SHADER_STORAGE_BUFFER, 2, 0);
glBindBufferBase (GL_SHADER_STORAGE_BUFFER, 1, 0);
glBindBufferBase (GL_SHADER_STORAGE_BUFFER, 0, 0);
}
void CullingSystem::JobReadback::resultFromBits( const Buffer& bufferVisBitsCurrent )
{
GLsizeiptr size = sizeof(int) * minDivide(m_numObjects,32);
glBindBuffer(GL_COPY_READ_BUFFER, bufferVisBitsCurrent.buffer );
glBindBuffer(GL_COPY_WRITE_BUFFER, m_bufferVisBitsReadback.buffer );
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, bufferVisBitsCurrent.offset, m_bufferVisBitsReadback.offset, size);
glBindBuffer( GL_COPY_READ_BUFFER, 0 );
glBindBuffer( GL_COPY_WRITE_BUFFER, 0 );
}
void CullingSystem::JobReadback::resultClient()
{
glBindBuffer(GL_COPY_WRITE_BUFFER, m_bufferVisBitsReadback.buffer);
glGetBufferSubData(GL_COPY_WRITE_BUFFER, m_bufferVisBitsReadback.offset, m_bufferVisBitsReadback.size, m_hostVisBits);
glBindBuffer( GL_COPY_WRITE_BUFFER, 0);
}
void CullingSystem::JobReadbackPersistent::resultFromBits(const Buffer& bufferVisBitsCurrent)
{
GLsizeiptr size = sizeof( int ) * minDivide( m_numObjects, 32 );
glCopyNamedBufferSubData( bufferVisBitsCurrent.buffer, m_bufferVisBitsReadback.buffer, bufferVisBitsCurrent.offset, m_bufferVisBitsReadback.offset, size);
if (m_fence) {
glDeleteSync( m_fence );
}
m_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
void CullingSystem::JobReadbackPersistent::resultClient()
{
if (m_fence) {
GLsizeiptr size = sizeof( int ) * minDivide( m_numObjects, 32 );
// as some samples read-back within same frame (not recommended) we use the flush here, normally one wouldnt use it
glClientWaitSync(m_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
glDeleteSync(m_fence);
m_fence = NULL;
memcpy( m_hostVisBits, ((uint8_t*)m_bufferVisBitsMapping) + m_bufferVisBitsReadback.offset, size );
}
}
================================================
FILE: cullingsystem.hpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef CULLINGSYSTEM_H__
#define CULLINGSYSTEM_H__
#include <cstddef>
#include <cstdint>
#include <nvgl/extensions_gl.hpp>
class CullingSystem {
public:
struct Programs {
GLuint object_frustum;
GLuint object_hiz;
GLuint object_raster;
GLuint bit_temporallast;
GLuint bit_temporalnew;
GLuint bit_regular;
GLuint depth_mips;
};
enum MethodType {
METHOD_FRUSTUM,
METHOD_HIZ,
METHOD_RASTER,
NUM_METHODS,
};
enum BitType {
BITS_CURRENT,
BITS_CURRENT_AND_LAST,
BITS_CURRENT_AND_NOT_LAST,
NUM_BITS,
};
struct Buffer {
GLuint buffer;
GLsizei stride;
GLintptr offset;
GLsizeiptr size;
void create( size_t sizei, const void* data, GLbitfield flags )
{
size = sizei;
offset = 0;
stride = 0;
glCreateBuffers( 1, &buffer );
glNamedBufferStorage( buffer, size, data, flags );
}
Buffer( GLuint buffer, size_t sizei = 0 )
: buffer( buffer )
, offset( 0 )
, stride( 0 )
{
if (!sizei) {
if (sizeof( GLsizeiptr ) > 4)
glGetNamedBufferParameteri64v( buffer, GL_BUFFER_SIZE, (GLint64*)&size );
else
glGetNamedBufferParameteriv( buffer, GL_BUFFER_SIZE, (GLint*)&size );
}
else {
size = sizei;
}
}
Buffer()
: buffer(0)
, stride(0)
, offset(0)
, size(0)
{
}
inline void BindBufferRange(GLenum target, GLuint index) const {
glBindBufferRange(target, index, buffer, offset, size);
}
inline void TexBuffer(GLenum target, GLenum internalformat) const {
glTexBufferRange(target, internalformat, buffer, offset, size);
}
inline void ClearBufferSubData(GLenum target,GLenum internalformat,GLenum format,GLenum type,const GLvoid* data) const {
glClearBufferSubData(target,internalformat,offset,size,format,type,data);
}
};
class Job {
public:
int m_numObjects;
// world-space matrices {mat4 world, mat4 worldInverseTranspose}
Buffer m_bufferMatrices;
Buffer m_bufferBboxes; // only used in dualindex mode (2 x vec4)
// 1 32-bit integer per object (index)
Buffer m_bufferObjectMatrix;
// object-space bounding box (2 x vec4)
// or 1 32-bit integer per object (dualindex mode)
Buffer m_bufferObjectBbox;
// 1 32-bit integer per object
Buffer m_bufferVisOutput;
// 1 32-bit integer per 32 objects (1 bit per object)
Buffer m_bufferVisBitsCurrent;
Buffer m_bufferVisBitsLast;
// for HiZ
GLuint m_textureDepthWithMipmaps;
// derive from this class and implement this function how you want to
// deal with the results that are provided in the buffer
virtual void resultFromBits( const Buffer& bufferVisBitsCurrent ) = 0;
// for readback methods we need to wait for a result
virtual void resultClient() {};
};
class JobReadback : public Job {
public:
// 1 32-bit integer per 32 objects (1 bit per object)
Buffer m_bufferVisBitsReadback;
uint32_t* m_hostVisBits;
// Do not use this Job class unless you have to. Persistent
// mapped buffers are preferred.
// Copies result into readback buffer
void resultFromBits( const Buffer& bufferVisBitsCurrent );
// getBufferData into hostVisBits (blocking!)
void resultClient();
};
class JobReadbackPersistent : public Job {
public:
// 1 32-bit integer per 32 objects (1 bit per object)
Buffer m_bufferVisBitsReadback;
void* m_bufferVisBitsMapping;
uint32_t* m_hostVisBits;
GLsync m_fence;
// Copies result into readback buffer and records
// a fence.
void resultFromBits(const Buffer& bufferVisBitsCurrent);
// waits on fence and copies mapping into hostVisBits
void resultClient();
};
// multidrawindirect based
class JobIndirectUnordered : public Job {
public:
GLuint m_program_indirect_compact;
// 1 indirectSize per object,
Buffer m_bufferObjectIndirects;
Buffer m_bufferIndirectResult;
// 1 integer
Buffer m_bufferIndirectCounter;
void resultFromBits( const Buffer& bufferVisBitsCurrent );
};
struct View {
const float* viewProjMatrix;
const float* viewDir;
const float* viewPos;
};
void init( const Programs &programs, bool dualindex );
void deinit();
void update( const Programs &programs, bool dualindex );
// helper function for HiZ method, leaves fbo bound to 0
void buildDepthMipmaps(GLuint textureDepth, int width, int height);
// assumes relevant fbo bound for raster method
void buildOutput( MethodType method, Job &job, const View& view );
void bitsFromOutput ( Job &job, BitType type );
void resultFromBits ( Job &job );
void resultClient ( Job &job );
// swaps the Current/Last bit array (for temporal coherent techniques)
void swapBits ( Job &job );
private:
struct Uniforms {
GLint depth_lod;
GLint depth_even;
GLint frustum_viewProj;
GLint hiz_viewProj;
GLint raster_viewProj;
GLint raster_viewDir;
GLint raster_viewPos;
};
void testBboxes( Job &job, bool raster);
Programs m_programs;
Uniforms m_uniforms;
GLuint m_fbo;
GLuint m_tbo[2];
bool m_dualindex;
bool m_useSSBO;
bool m_useRepesentativeTest;
};
#endif
================================================
FILE: nodetree.cpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#include "nodetree.hpp"
#include <assert.h>
//////////////////////////////////////////////////////////////////////////
static inline void clearNode(NodeTree::Node &node)
{
node.level = -1;
node.leafidx = NodeTree::INVALID;
node.levelidx = NodeTree::INVALID;
node.parentidx = NodeTree::INVALID;
node.childidx = NodeTree::INVALID;
node.siblingidx = NodeTree::INVALID;
}
NodeTree::NodeTree()
{
m_levelsUsed = 0;
m_treeCompactChangeID = 0;
m_nodesActive = 0;
clearNode(m_root);
m_root.levelidx = 0;
m_root.level = -1;
}
const NodeTree::Level* NodeTree::getUsedLevel( int level ) const
{
if (0 <= level && level < m_levelsUsed){
return &m_levels[level];
}
return nullptr;
}
unsigned int NodeTree::getTreeParentChangeID() const
{
return m_treeCompactChangeID;
}
const std::vector<NodeTree::compactID>& NodeTree::getTreeCompactNodes() const
{
return m_treeCompactNodes;
}
NodeTree::nodeID NodeTree::createNode()
{
nodeID id;
if (!m_unusedNodes.empty()){
id = m_unusedNodes[m_unusedNodes.size()-1];
m_unusedNodes.pop_back();
}
else{
Node node;
m_nodes.push_back(node);
m_treeCompactNodes.push_back(compactID());
id = (nodeID)(m_nodes.size()-1);
}
Node& node = getNode(id);
clearNode(node);
return id;
}
void NodeTree::deleteNode( nodeID nodeidx )
{
assert (isValid(nodeidx) && nodeidx != ROOT);
const Node &node = getNode(nodeidx);
// make children unlinked
while (isValid(node.childidx)){
setNodeParent(node.childidx,INVALID);
}
// remove self from parent list
setNodeParent(nodeidx,INVALID);
m_unusedNodes.push_back(nodeidx);
}
void NodeTree::setNodeParent( nodeID nodeidx, nodeID parentidx )
{
assert (isValid(nodeidx) && nodeidx != ROOT);
Node &node = getNode(nodeidx);
if (node.parentidx == parentidx)
return;
if (isValid(node.parentidx)){
// unlink from old
Node& parent = getNode(node.parentidx);
bool found = false;
if (parent.childidx == nodeidx){
parent.childidx = node.siblingidx;
found = true;
}
else if (isValid(parent.childidx)){
nodeID child = parent.childidx;
while(isValid(getNode(child).siblingidx)){
if (getNode(child).siblingidx == nodeidx){
getNode(child).siblingidx = node.siblingidx;
found = true;
break;
}
child = getNode(child).siblingidx;
}
}
assert(found && "node was not a child of parent");
node.siblingidx = INVALID;
updateLeafNode(node.parentidx);
}
if (isValid(parentidx)){
// link to new
Node& parent = getNode(parentidx);
node.siblingidx = parent.childidx;
parent.childidx = nodeidx;
updateLeafNode(node.parentidx);
}
if (isNodeInTree(nodeidx)){
updateLevelNode(nodeidx, isNodeInTree(parentidx) ? parentidx : INVALID);
}
node.parentidx = parentidx;
}
void NodeTree::addToTree( nodeID nodeidx )
{
assert (isValid(nodeidx) && nodeidx != ROOT);
const Node &node = getNode(nodeidx);
assert (!isNodeInTree(nodeidx) && "must not be already added to tree");
assert ( isNodeInTree(node.parentidx) && "parent must be already added to tree");
updateLevelNode(nodeidx,node.parentidx);
}
void NodeTree::removeFromTree( nodeID nodeidx )
{
assert (isValid(nodeidx) && nodeidx != ROOT);
const Node &node = getNode(nodeidx);
assert (isNodeInTree(nodeidx) && "must be already added to tree");
updateLevelNode(nodeidx,INVALID);
}
void NodeTree::addToLevel( nodeID nodeidx, nodeID parentidx )
{
Node& node = getNode(nodeidx);
const Node& parent = getNode(parentidx);
Level& level = getLevel(parent.level+1);
level.changeID++;
node.levelidx = (lvlID)level.nodes.size();
node.level = parent.level+1;
level.nodes.push_back(nodeidx);
if (!isValid(node.childidx)){
addLeafNode(nodeidx);
}
m_levelsUsed = node.level+1 > m_levelsUsed ? node.level+1 : m_levelsUsed;
m_nodesActive++;
}
void NodeTree::removeFromLevel( nodeID nodeidx )
{
Node& node = getNode(nodeidx);
Level& level = getLevel(node.level);
level.changeID++;
level.nodes[node.levelidx] = level.nodes[level.nodes.size()-1];
getNode(level.nodes[node.levelidx]).levelidx = node.levelidx;
level.nodes.pop_back();
if (isValid(node.leafidx)){
removeLeafNode(nodeidx);
}
if (node.level+1 == m_levelsUsed && level.nodes.empty()){
m_levelsUsed--;
}
node.level = -1;
node.levelidx = INVALID;
node.leafidx = INVALID;
m_nodesActive--;
}
void NodeTree::removeLeafNode( nodeID nodeidx )
{
assert(isNodeInTree(nodeidx));
Node& node = getNode(nodeidx);
Level& level = getLevel(node.level);
// remove
level.leaves[node.leafidx] = level.leaves[level.leaves.size()-1];
getNode(level.leaves[node.leafidx]).leafidx = node.leafidx;
level.leaves.pop_back();
}
void NodeTree::addLeafNode( nodeID nodeidx )
{
assert(isNodeInTree(nodeidx));
Node& node = getNode(nodeidx);
Level& level = getLevel(node.level);
// add
node.leafidx = (lvlID)level.leaves.size();
level.leaves.push_back(nodeidx);
}
void NodeTree::updateLeafNode( nodeID nodeidx )
{
if (!isNodeInTree(nodeidx))
return;
Node& node = getNode(nodeidx);
if (!isValid(node.childidx) && isValid(node.leafidx)){
removeLeafNode(nodeidx);
}
else if (isValid(node.childidx) && !isValid(node.leafidx)){
addLeafNode(nodeidx);
}
}
void NodeTree::updateLevelNode( nodeID nodeidx, nodeID parentidx )
{
// at this point node.parentidx is still the old value
Node &node = getNode(nodeidx);
// update level parent buffer to reflect last state always
m_treeCompactNodes[nodeidx].parent = parentidx;
m_treeCompactChangeID++;
if (isValid(node.levelidx)){
// already active
if (isValid(parentidx)){
const Node& parent = getNode(parentidx);
int oldlevel = node.level;
int newlevel = parent.level + 1;
// we remain in the same level and only our parent has changed
if (oldlevel == newlevel){
return;
}
removeFromLevel(nodeidx);
addToLevel(nodeidx,parentidx);
}
else{
removeFromLevel(nodeidx);
}
}
else if (isValid(parentidx)){
// was inactive
// add to level
addToLevel(nodeidx,parentidx);
}
m_treeCompactNodes[nodeidx].level = node.level;
nodeID child = node.childidx;
while (isValid(child)){
updateLevelNode(child, isValid(parentidx) ? nodeidx : INVALID );
child = getNode(child).siblingidx;
}
}
void NodeTree::reserve( int numNodes )
{
m_nodes.reserve( numNodes );
m_treeCompactNodes.reserve( numNodes );
}
void NodeTree::create( int numNodes )
{
Node node;
clearNode(node);
m_nodes.resize( numNodes, node );
m_treeCompactNodes.resize( numNodes, compactID() );
}
void NodeTree::clear()
{
m_nodesActive = 0;
m_levelsUsed = 0;
m_treeCompactChangeID = 0;
m_levels.clear();
m_nodes.clear();
m_treeCompactNodes.clear();
}
================================================
FILE: nodetree.hpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#pragma once
#include <vector>
class NodeTree {
public:
enum Flags {
INVALID = 0xFFFFFFFF,
ROOT = 0x7FFFFFFF,
LEVELBITS = 8,
PARENTBITS = 32 - LEVELBITS
};
static constexpr unsigned INVALID_LEVEL = (1 << LEVELBITS) - 1;
static constexpr unsigned INVALID_PARENT = (1 << PARENTBITS) - 1;
struct compactID {
unsigned level : LEVELBITS;
unsigned parent : PARENTBITS;
compactID(){
level = INVALID_LEVEL;
parent = INVALID_PARENT;
}
};
typedef unsigned int nodeID;
typedef unsigned int lvlID;
struct Level {
unsigned int changeID;
std::vector<nodeID> nodes;
std::vector<nodeID> leaves;
Level(){
changeID = 0;
}
};
struct Node {
nodeID parentidx;
lvlID levelidx;
lvlID leafidx;
int level;
nodeID childidx;
nodeID siblingidx;
};
private:
Node m_root;
// general nodes
std::vector<Node> m_nodes;
std::vector<nodeID> m_unusedNodes;
// actual nodes added to tree
std::vector<compactID> m_treeCompactNodes;
std::vector<Level> m_levels;
unsigned int m_treeCompactChangeID;
int m_nodesActive;
int m_levelsUsed;
public:
NodeTree();
const Level* getUsedLevel(int level) const;
inline int getNumUsedLevel() const
{
return m_levelsUsed;
}
unsigned int getTreeParentChangeID() const;
const std::vector<compactID>& getTreeCompactNodes() const;
inline nodeID getTreeRoot()
{
return ROOT;
}
inline const Node& getNode(nodeID nodeidx) const
{
if (nodeidx == ROOT) return m_root;
else return m_nodes[nodeidx];
}
inline bool isValid(unsigned int id)
{
return id != INVALID;
}
inline bool isNodeInTree(nodeID nodeidx)
{
return isValid(nodeidx) && isValid(getNode(nodeidx).levelidx);
}
inline nodeID getParentNode(nodeID nodeidx) const
{
return getNode(nodeidx).parentidx;
}
nodeID createNode();
void deleteNode(nodeID nodeidx);
void setNodeParent(nodeID nodeidx, nodeID parentidx);
void addToTree(nodeID nodeidx);
void removeFromTree(nodeID nodeidx);
void reserve(int numNodes);
void create(int numNodes);
void clear();
int getNumActiveNodes() const {
return m_nodesActive;
}
private:
inline Level& getLevel(int level)
{
if ((int)m_levels.size() < level+1){
m_levels.resize(level+1);
}
return m_levels[level];
}
inline Node& getNode(nodeID nodeidx)
{
if (nodeidx == ROOT) return m_root;
else return m_nodes[nodeidx];
}
void addToLevel(nodeID nodeidx, nodeID parentidx);
void removeFromLevel(nodeID nodeidx);
void removeLeafNode(nodeID nodeidx);
void addLeafNode(nodeID nodeidx);
void updateLeafNode(nodeID nodeidx);
void updateLevelNode(nodeID nodeidx, nodeID parentidx);
};
================================================
FILE: nvtoken.cpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#include "nvtoken.hpp"
namespace nvtoken
{
//////////////////////////////////////////////////////////////////////////
// generic
GLuint s_nvcmdlist_header[NVTOKEN_TYPES] = {0};
GLuint s_nvcmdlist_headerSizes[NVTOKEN_TYPES] = {0};
GLushort s_nvcmdlist_stages[NVTOKEN_STAGES] = {0};
bool s_nvcmdlist_bindless = false;
static inline GLuint nvtokenHeaderSW(GLuint type, GLuint size){
return type | (size<<16);
}
static inline GLenum nvtokenHeaderCommandSW(GLuint header)
{
return header & 0xFFFF;
}
static inline GLuint nvtokenHeaderSizeSW(GLuint header)
{
return header>>16;
}
static inline GLenum nvtokenHeaderCommand(GLuint header)
{
for (int i = 0; i < NVTOKEN_TYPES; i++){
if (header == s_nvcmdlist_header[i]) return i;
}
assert(0 && "can't find header");
return -1;
}
template <class T>
static void nvtokenRegisterSize()
{
s_nvcmdlist_headerSizes[T::ID] = sizeof(T);
}
void nvtokenInitInternals( bool hwsupport, bool bindlessSupport)
{
assert( !hwsupport || (hwsupport && bindlessSupport) );
nvtokenRegisterSize<NVTokenTerminate>();
nvtokenRegisterSize<NVTokenNop>();
nvtokenRegisterSize<NVTokenDrawElems>();
nvtokenRegisterSize<NVTokenDrawArrays>();
nvtokenRegisterSize<NVTokenDrawElemsStrip>();
nvtokenRegisterSize<NVTokenDrawArraysStrip>();
nvtokenRegisterSize<NVTokenDrawElemsInstanced>();
nvtokenRegisterSize<NVTokenDrawArraysInstanced>();
nvtokenRegisterSize<NVTokenVbo>();
nvtokenRegisterSize<NVTokenIbo>();
nvtokenRegisterSize<NVTokenUbo>();
nvtokenRegisterSize<NVTokenLineWidth>();
nvtokenRegisterSize<NVTokenPolygonOffset>();
nvtokenRegisterSize<NVTokenScissor>();
nvtokenRegisterSize<NVTokenBlendColor>();
nvtokenRegisterSize<NVTokenViewport>();
nvtokenRegisterSize<NVTokenAlphaRef>();
nvtokenRegisterSize<NVTokenStencilRef>();
nvtokenRegisterSize<NVTokenFrontFace>();
for (int i = 0; i < NVTOKEN_TYPES; i++){
GLuint sz = s_nvcmdlist_headerSizes[i];
assert(sz);
}
s_nvcmdlist_bindless = bindlessSupport;
if (hwsupport){
for (int i = 0; i < NVTOKEN_TYPES; i++){
s_nvcmdlist_header[i] = glGetCommandHeaderNV(i,s_nvcmdlist_headerSizes[i]);
}
s_nvcmdlist_stages[NVTOKEN_STAGE_VERTEX] = glGetStageIndexNV(GL_VERTEX_SHADER);
s_nvcmdlist_stages[NVTOKEN_STAGE_TESS_CONTROL] = glGetStageIndexNV(GL_TESS_CONTROL_SHADER);
s_nvcmdlist_stages[NVTOKEN_STAGE_TESS_EVALUATION] = glGetStageIndexNV(GL_TESS_EVALUATION_SHADER);
s_nvcmdlist_stages[NVTOKEN_STAGE_GEOMETRY] = glGetStageIndexNV(GL_GEOMETRY_SHADER);
s_nvcmdlist_stages[NVTOKEN_STAGE_FRAGMENT] = glGetStageIndexNV(GL_FRAGMENT_SHADER);
}
else{
for (int i = 0; i < NVTOKEN_TYPES; i++){
s_nvcmdlist_header[i] = nvtokenHeaderSW(i,s_nvcmdlist_headerSizes[i]);
}
for (int i = 0; i < NVTOKEN_STAGES; i++){
s_nvcmdlist_stages[i] = i;
}
}
}
#define TOSTRING(a) case a: return #a;
const char* nvtokenCommandToString(GLenum type){
switch (type){
TOSTRING(GL_NOP_COMMAND_NV );
TOSTRING(GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV);
TOSTRING(GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV );
TOSTRING(GL_ELEMENT_ADDRESS_COMMAND_NV );
TOSTRING(GL_ATTRIBUTE_ADDRESS_COMMAND_NV );
TOSTRING(GL_UNIFORM_ADDRESS_COMMAND_NV );
TOSTRING(GL_BLEND_COLOR_COMMAND_NV );
TOSTRING(GL_STENCIL_REF_COMMAND_NV );
TOSTRING(GL_TERMINATE_SEQUENCE_COMMAND_NV );
TOSTRING(GL_LINE_WIDTH_COMMAND_NV );
TOSTRING(GL_POLYGON_OFFSET_COMMAND_NV );
TOSTRING(GL_ALPHA_REF_COMMAND_NV );
TOSTRING(GL_VIEWPORT_COMMAND_NV );
TOSTRING(GL_SCISSOR_COMMAND_NV );
TOSTRING(GL_DRAW_ELEMENTS_COMMAND_NV );
TOSTRING(GL_DRAW_ARRAYS_COMMAND_NV );
TOSTRING(GL_DRAW_ELEMENTS_STRIP_COMMAND_NV );
TOSTRING(GL_DRAW_ARRAYS_STRIP_COMMAND_NV );
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
void nvtokenGetStats( const void* NV_RESTRICT stream, size_t streamSize, int stats[NVTOKEN_TYPES] )
{
const GLubyte* NV_RESTRICT current = (GLubyte*)stream;
const GLubyte* streamEnd = current + streamSize;
while (current < streamEnd){
const GLuint* header = (const GLuint*)current;
GLenum type = nvtokenHeaderCommand(*header);
stats[type]++;
current += s_nvcmdlist_headerSizes[type];
}
}
// Emulation related
static inline GLenum nvtokenDrawCommandSequenceSW( const void* NV_RESTRICT stream, size_t streamSize, GLenum mode, GLenum type, const StateSystem::State& state )
{
const GLubyte* NV_RESTRICT current = (GLubyte*)stream;
const GLubyte* streamEnd = current + streamSize;
GLenum modeStrip;
if (mode == GL_LINES) modeStrip = GL_LINE_STRIP;
else if (mode == GL_TRIANGLES) modeStrip = GL_TRIANGLE_STRIP;
/*else if (mode == GL_QUADS) modeStrip = GL_QUAD_STRIP;*/
else if (mode == GL_LINES_ADJACENCY) modeStrip = GL_LINE_STRIP_ADJACENCY;
else if (mode == GL_TRIANGLES_ADJACENCY) modeStrip = GL_TRIANGLE_STRIP_ADJACENCY;
else modeStrip = mode;
GLenum modeSpecial;
if (mode == GL_LINES) modeSpecial = GL_LINE_LOOP;
else if (mode == GL_TRIANGLES) modeSpecial = GL_TRIANGLE_FAN;
else modeSpecial = mode;
while (current < streamEnd){
const GLuint* header = (const GLuint*)current;
GLenum cmdtype = nvtokenHeaderCommand(*header);
// if you always use emulation on non-native tokens you can use
// cmdtype = nvtokenHeaderCommandSW(header->encoded)
switch(cmdtype){
case GL_TERMINATE_SEQUENCE_COMMAND_NV:
{
return type;
}
break;
case GL_NOP_COMMAND_NV:
{
}
break;
case GL_DRAW_ELEMENTS_COMMAND_NV:
{
const DrawElementsCommandNV* cmd = (const DrawElementsCommandNV*)current;
glDrawElementsBaseVertex(mode, cmd->count, type, (const GLvoid*)(cmd->firstIndex * sizeof(GLuint)), cmd->baseVertex);
}
break;
case GL_DRAW_ARRAYS_COMMAND_NV:
{
const DrawArraysCommandNV* cmd = (const DrawArraysCommandNV*)current;
glDrawArrays(mode, cmd->first, cmd->count);
}
break;
case GL_DRAW_ELEMENTS_STRIP_COMMAND_NV:
{
const DrawElementsCommandNV* cmd = (const DrawElementsCommandNV*)current;
glDrawElementsBaseVertex(modeStrip, cmd->count, type, (const GLvoid*)(cmd->firstIndex * sizeof(GLuint)), cmd->baseVertex);
}
break;
case GL_DRAW_ARRAYS_STRIP_COMMAND_NV:
{
const DrawArraysCommandNV* cmd = (const DrawArraysCommandNV*)current;
glDrawArrays(modeStrip, cmd->first, cmd->count);
}
break;
case GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV:
{
const DrawElementsInstancedCommandNV* cmd = (const DrawElementsInstancedCommandNV*)current;
assert (cmd->mode == mode || cmd->mode == modeStrip || cmd->mode == modeSpecial);
glDrawElementsIndirect(cmd->mode, type, &cmd->count);
}
break;
case GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV:
{
const DrawArraysInstancedCommandNV* cmd = (const DrawArraysInstancedCommandNV*)current;
assert (cmd->mode == mode || cmd->mode == modeStrip || cmd->mode == modeSpecial);
glDrawArraysIndirect(cmd->mode, &cmd->count);
}
break;
case GL_ELEMENT_ADDRESS_COMMAND_NV:
{
const ElementAddressCommandNV* cmd = (const ElementAddressCommandNV*)current;
type = cmd->typeSizeInByte == 4 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
if (s_nvcmdlist_bindless){
glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, GLuint64(cmd->addressLo) | (GLuint64(cmd->addressHi)<<32), 0x7FFFFFFF);
}
else{
const ElementAddressCommandEMU* cmd = (const ElementAddressCommandEMU*)current;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cmd->buffer);
}
}
break;
case GL_ATTRIBUTE_ADDRESS_COMMAND_NV:
{
if (s_nvcmdlist_bindless){
const AttributeAddressCommandNV* cmd = (const AttributeAddressCommandNV*)current;
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, cmd->index, GLuint64(cmd->addressLo) | (GLuint64(cmd->addressHi)<<32), 0x7FFFFFFF);
}
else{
const AttributeAddressCommandEMU* cmd = (const AttributeAddressCommandEMU*)current;
glBindVertexBuffer(cmd->index, cmd->buffer, cmd->offset, state.vertexformat.bindings[cmd->index].stride);
}
}
break;
case GL_UNIFORM_ADDRESS_COMMAND_NV:
{
if (s_nvcmdlist_bindless){
const UniformAddressCommandNV* cmd = (const UniformAddressCommandNV*)current;
glBufferAddressRangeNV(GL_UNIFORM_BUFFER_ADDRESS_NV, cmd->index, GLuint64(cmd->addressLo) | (GLuint64(cmd->addressHi)<<32), 0x10000);
}
else{
const UniformAddressCommandEMU* cmd = (const UniformAddressCommandEMU*)current;
glBindBufferRange(GL_UNIFORM_BUFFER,cmd->index, cmd->buffer, cmd->offset256 * 256, cmd->size4*4);
}
}
break;
case GL_BLEND_COLOR_COMMAND_NV:
{
const BlendColorCommandNV* cmd = (const BlendColorCommandNV*)current;
glBlendColor(cmd->red,cmd->green,cmd->blue,cmd->alpha);
}
break;
case GL_STENCIL_REF_COMMAND_NV:
{
const StencilRefCommandNV* cmd = (const StencilRefCommandNV*)current;
glStencilFuncSeparate(GL_FRONT, state.stencil.funcs[StateSystem::FACE_FRONT].func, cmd->frontStencilRef, state.stencil.funcs[StateSystem::FACE_FRONT].mask);
glStencilFuncSeparate(GL_BACK, state.stencil.funcs[StateSystem::FACE_BACK ].func, cmd->backStencilRef, state.stencil.funcs[StateSystem::FACE_BACK ].mask);
}
break;
case GL_LINE_WIDTH_COMMAND_NV:
{
const LineWidthCommandNV* cmd = (const LineWidthCommandNV*)current;
glLineWidth(cmd->lineWidth);
}
break;
case GL_POLYGON_OFFSET_COMMAND_NV:
{
const PolygonOffsetCommandNV* cmd = (const PolygonOffsetCommandNV*)current;
glPolygonOffset(cmd->scale,cmd->bias);
}
break;
case GL_ALPHA_REF_COMMAND_NV:
{/*
const AlphaRefCommandNV* cmd = (const AlphaRefCommandNV*)current;
glAlphaFunc(state.alpha.mode, cmd->alphaRef);
*/
}
break;
case GL_VIEWPORT_COMMAND_NV:
{
const ViewportCommandNV* cmd = (const ViewportCommandNV*)current;
glViewport(cmd->x, cmd->y, cmd->width, cmd->height);
}
break;
case GL_SCISSOR_COMMAND_NV:
{
const ScissorCommandNV* cmd = (const ScissorCommandNV*)current;
glScissor(cmd->x,cmd->y,cmd->width,cmd->height);
}
break;
case GL_FRONT_FACE_COMMAND_NV:
{
FrontFaceCommandNV* cmd = (FrontFaceCommandNV*)current;
glFrontFace(cmd->frontFace?GL_CW:GL_CCW);
}
break;
}
GLuint tokenSize = s_nvcmdlist_headerSizes[cmdtype];
assert(tokenSize);
current += tokenSize;
}
return type;
}
void nvtokenDrawCommandsSW(GLenum mode, const void* NV_RESTRICT stream, size_t streamSize,
const GLintptr* NV_RESTRICT offsets, const GLsizei* NV_RESTRICT sizes,
GLuint count,
StateSystem::State &state)
{
const char* NV_RESTRICT tokens = (const char*)stream;
GLenum type = GL_UNSIGNED_SHORT;
for (GLuint i = 0; i < count; i++)
{
size_t offset = offsets[i];
size_t size = sizes[i];
assert(size + offset <= streamSize);
type = nvtokenDrawCommandSequenceSW(&tokens[offset], size, mode, type, state);
}
}
#if NVTOKEN_STATESYSTEM
void nvtokenDrawCommandsStatesSW(const void* NV_RESTRICT stream, size_t streamSize,
const GLintptr* NV_RESTRICT offsets, const GLsizei* NV_RESTRICT sizes,
const GLuint* NV_RESTRICT states, const GLuint* NV_RESTRICT fbos, GLuint count,
StateSystem &stateSystem)
{
int lastFbo = ~0;
const char* NV_RESTRICT tokens = (const char*)stream;
StateSystem::StateID lastID;
GLenum type = GL_UNSIGNED_SHORT;
for (GLuint i = 0; i < count; i++)
{
GLuint fbo;
StateSystem::StateID curID = states[i];
const StateSystem::State& state = stateSystem.get(curID);
if (fbos[i]){
fbo = fbos[i];
}
else{
fbo = state.fbo.fboDraw;
}
if (fbo != lastFbo){
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
lastFbo = fbo;
}
if (i == 0){
stateSystem.applyGL( curID, true ); // quite costly
}
else {
stateSystem.applyGL( curID, lastID, true );
}
lastID = curID;
size_t offset = offsets[i];
size_t size = sizes[i];
GLenum mode = state.basePrimitiveMode;
assert(size + offset <= streamSize);
type = nvtokenDrawCommandSequenceSW(&tokens[offset], size, mode, type, state);
}
}
#endif
}
================================================
FILE: nvtoken.hpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#include <assert.h>
#include <string>
#include <vector>
#define NVTOKEN_STATESYSTEM 1
#include "platform.h"
#include <nvgl/extensions_gl.hpp>
#if NVTOKEN_STATESYSTEM
// not needed if emulation is not used, or implemented differently
#include "statesystem.hpp"
#else
namespace StateSystem {
// Minimal emulation layer
enum Faces {
FACE_FRONT,
FACE_BACK,
MAX_FACES,
};
struct State {
struct {
struct {
GLsizei stride;
}bindings[16];
}vertexformat;
struct {
GLenum mode;
}alpha;
struct {
struct {
GLenum func;
GLuint mask;
}funcs[MAX_FACES];
}stencil;
};
}
#endif
namespace nvtoken
{
//////////////////////////////////////////////////////////////////////////
// generic
// not the cleanest way
#define NVTOKEN_TYPES (GL_FRONT_FACE_COMMAND_NV+1)
enum NVTokenShaderStage {
NVTOKEN_STAGE_VERTEX,
NVTOKEN_STAGE_TESS_CONTROL,
NVTOKEN_STAGE_TESS_EVALUATION,
NVTOKEN_STAGE_GEOMETRY,
NVTOKEN_STAGE_FRAGMENT,
NVTOKEN_STAGES,
};
extern bool s_nvcmdlist_bindless;
extern GLuint s_nvcmdlist_header[NVTOKEN_TYPES];
extern GLuint s_nvcmdlist_headerSizes[NVTOKEN_TYPES];
extern GLushort s_nvcmdlist_stages[NVTOKEN_STAGES];
class NVPointerStream {
public:
size_t m_max;
unsigned char* m_begin;
unsigned char* m_end;
unsigned char* NV_RESTRICT m_cur;
void init(void* data, size_t size)
{
m_begin = (unsigned char*)data;
m_end = m_begin + size;
m_cur = m_begin;
m_max = size;
}
size_t size() const
{
return m_cur - m_begin;
}
size_t capacity() const
{
return m_max;
}
};
struct NVTokenSequence {
std::vector<GLintptr> offsets;
std::vector<GLsizei> sizes;
std::vector<GLuint> states;
std::vector<GLuint> fbos;
};
#pragma pack(push,1)
typedef struct {
GLuint header;
GLuint buffer;
GLuint _pad;
GLuint typeSizeInByte;
} ElementAddressCommandEMU;
typedef struct {
GLuint header;
GLuint index;
GLuint buffer;
GLuint offset;
} AttributeAddressCommandEMU;
typedef struct {
GLuint header;
GLushort index;
GLushort stage;
GLuint buffer;
GLushort offset256;
GLushort size4;
} UniformAddressCommandEMU;
struct NVTokenNop {
static const GLenum ID = GL_NOP_COMMAND_NV;
NOPCommandNV cmd;
NVTokenNop() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenTerminate {
static const GLenum ID = GL_TERMINATE_SEQUENCE_COMMAND_NV;
TerminateSequenceCommandNV cmd;
NVTokenTerminate() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenDrawElemsInstanced {
static const GLenum ID = GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV;
DrawElementsInstancedCommandNV cmd;
NVTokenDrawElemsInstanced() {
cmd.mode = GL_TRIANGLES;
cmd.baseInstance = 0;
cmd.baseVertex = 0;
cmd.firstIndex = 0;
cmd.count = 0;
cmd.instanceCount = 1;
cmd.header = s_nvcmdlist_header[ID];
}
void setMode(GLenum primmode) {
cmd.mode = primmode;
}
void setParams(GLuint count, GLuint firstIndex=0, GLuint baseVertex=0)
{
cmd.count = count;
cmd.firstIndex = firstIndex;
cmd.baseVertex = baseVertex;
}
void setInstances(GLuint count, GLuint baseInstance=0){
cmd.baseInstance = baseInstance;
cmd.instanceCount = count;
}
};
struct NVTokenDrawArraysInstanced {
static const GLenum ID = GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV;
DrawArraysInstancedCommandNV cmd;
NVTokenDrawArraysInstanced() {
cmd.mode = GL_TRIANGLES;
cmd.baseInstance = 0;
cmd.first = 0;
cmd.count = 0;
cmd.instanceCount = 1;
cmd.header = s_nvcmdlist_header[ID];
}
void setMode(GLenum primmode) {
cmd.mode = primmode;
}
void setParams(GLuint count, GLuint first=0)
{
cmd.count = count;
cmd.first = first;
}
void setInstances(GLuint count, GLuint baseInstance=0){
cmd.baseInstance = baseInstance;
cmd.instanceCount = count;
}
};
struct NVTokenDrawElems {
static const GLenum ID = GL_DRAW_ELEMENTS_COMMAND_NV;
DrawElementsCommandNV cmd;
NVTokenDrawElems() {
cmd.baseVertex = 0;
cmd.firstIndex = 0;
cmd.count = 0;
cmd.header = s_nvcmdlist_header[ID];
}
void setParams(GLuint count, GLuint firstIndex=0, GLuint baseVertex=0)
{
cmd.count = count;
cmd.firstIndex = firstIndex;
cmd.baseVertex = baseVertex;
}
void setMode(GLenum primmode) {
assert(primmode != GL_TRIANGLE_FAN && /* primmode != GL_POLYGON && */ primmode != GL_LINE_LOOP);
if (primmode == GL_LINE_STRIP || primmode == GL_TRIANGLE_STRIP || /* primmode == GL_QUAD_STRIP || */
primmode == GL_LINE_STRIP_ADJACENCY || primmode == GL_TRIANGLE_STRIP_ADJACENCY)
{
cmd.header = s_nvcmdlist_header[GL_DRAW_ELEMENTS_STRIP_COMMAND_NV];
}
else
{
cmd.header = s_nvcmdlist_header[GL_DRAW_ELEMENTS_COMMAND_NV];
}
}
};
struct NVTokenDrawArrays {
static const GLenum ID = GL_DRAW_ARRAYS_COMMAND_NV;
DrawArraysCommandNV cmd;
NVTokenDrawArrays() {
cmd.first = 0;
cmd.count = 0;
cmd.header = s_nvcmdlist_header[ID];
}
void setParams(GLuint count, GLuint first=0)
{
cmd.count = count;
cmd.first = first;
}
void setMode(GLenum primmode) {
assert(primmode != GL_TRIANGLE_FAN && /* primmode != GL_POLYGON && */ primmode != GL_LINE_LOOP);
if (primmode == GL_LINE_STRIP || primmode == GL_TRIANGLE_STRIP || /* primmode == GL_QUAD_STRIP || */
primmode == GL_LINE_STRIP_ADJACENCY || primmode == GL_TRIANGLE_STRIP_ADJACENCY)
{
cmd.header = s_nvcmdlist_header[GL_DRAW_ARRAYS_STRIP_COMMAND_NV];
}
else
{
cmd.header = s_nvcmdlist_header[GL_DRAW_ARRAYS_COMMAND_NV];
}
}
};
struct NVTokenDrawElemsStrip {
static const GLenum ID = GL_DRAW_ELEMENTS_STRIP_COMMAND_NV;
DrawElementsCommandNV cmd;
NVTokenDrawElemsStrip() {
cmd.baseVertex = 0;
cmd.firstIndex = 0;
cmd.count = 0;
cmd.header = s_nvcmdlist_header[ID];
}
void setParams(GLuint count, GLuint firstIndex=0, GLuint baseVertex=0)
{
cmd.count = count;
cmd.firstIndex = firstIndex;
cmd.baseVertex = baseVertex;
}
};
struct NVTokenDrawArraysStrip {
static const GLenum ID = GL_DRAW_ARRAYS_STRIP_COMMAND_NV;
DrawArraysCommandNV cmd;
NVTokenDrawArraysStrip() {
cmd.first = 0;
cmd.count = 0;
cmd.header = s_nvcmdlist_header[ID];
}
void setParams(GLuint count, GLuint first=0)
{
cmd.count = count;
cmd.first = first;
}
};
struct NVTokenVbo {
static const GLenum ID = GL_ATTRIBUTE_ADDRESS_COMMAND_NV;
union {
AttributeAddressCommandNV cmd;
AttributeAddressCommandEMU cmdEMU;
};
void setBinding(GLuint idx){
cmd.index = idx;
}
void setBuffer(GLuint buffer, GLuint64 address, GLuint offset)
{
if (s_nvcmdlist_bindless){
address += offset;
cmd.addressLo = GLuint(address & 0xFFFFFFFF);
cmd.addressHi = GLuint(address >> 32);
}
else{
cmdEMU.buffer = buffer;
cmdEMU.offset = offset;
}
}
NVTokenVbo() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenIbo {
static const GLenum ID = GL_ELEMENT_ADDRESS_COMMAND_NV;
union{
ElementAddressCommandNV cmd;
ElementAddressCommandEMU cmdEMU;
};
void setType(GLenum type){
if (type == GL_UNSIGNED_BYTE){
cmd.typeSizeInByte = 1;
}
else if (type == GL_UNSIGNED_SHORT){
cmd.typeSizeInByte = 2;
}
else if (type == GL_UNSIGNED_INT){
cmd.typeSizeInByte = 4;
}
else{
assert(0 && "illegal type");
}
}
void setBuffer(GLuint buffer, GLuint64 address)
{
if (s_nvcmdlist_bindless){
cmd.addressLo = GLuint(address & 0xFFFFFFFF);
cmd.addressHi = GLuint(address >> 32);
}
else{
cmdEMU.buffer = buffer;
cmdEMU._pad = 0;
}
}
NVTokenIbo() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenUbo {
static const GLenum ID = GL_UNIFORM_ADDRESS_COMMAND_NV;
union{
UniformAddressCommandNV cmd;
UniformAddressCommandEMU cmdEMU;
};
void setBuffer(GLuint buffer, GLuint64 address, GLuint offset, GLuint size)
{
assert(size % 4 == 0 && offset % 256 == 0);
if (s_nvcmdlist_bindless){
address += offset;
cmd.addressLo = GLuint(address & 0xFFFFFFFF);
cmd.addressHi = GLuint(address >> 32);
}
else{
cmdEMU.buffer = buffer;
cmdEMU.offset256 = offset / 256;
cmdEMU.size4 = size / 4;
}
}
void setBinding(GLuint idx, NVTokenShaderStage stage){
cmd.index = idx;
cmd.stage = s_nvcmdlist_stages[stage];
}
NVTokenUbo() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenBlendColor{
static const GLenum ID = GL_BLEND_COLOR_COMMAND_NV;
BlendColorCommandNV cmd;
NVTokenBlendColor() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenStencilRef{
static const GLenum ID = GL_STENCIL_REF_COMMAND_NV;
StencilRefCommandNV cmd;
NVTokenStencilRef() {
cmd.header = s_nvcmdlist_header[ID];
}
} ;
struct NVTokenLineWidth{
static const GLenum ID = GL_LINE_WIDTH_COMMAND_NV;
LineWidthCommandNV cmd;
NVTokenLineWidth() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenPolygonOffset{
static const GLenum ID = GL_POLYGON_OFFSET_COMMAND_NV;
PolygonOffsetCommandNV cmd;
NVTokenPolygonOffset() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenAlphaRef{
static const GLenum ID = GL_ALPHA_REF_COMMAND_NV;
AlphaRefCommandNV cmd;
NVTokenAlphaRef() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenViewport{
static const GLenum ID = GL_VIEWPORT_COMMAND_NV;
ViewportCommandNV cmd;
NVTokenViewport() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenScissor {
static const GLenum ID = GL_SCISSOR_COMMAND_NV;
ScissorCommandNV cmd;
NVTokenScissor() {
cmd.header = s_nvcmdlist_header[ID];
}
};
struct NVTokenFrontFace {
static const GLenum ID = GL_FRONT_FACE_COMMAND_NV;
FrontFaceCommandNV cmd;
NVTokenFrontFace() {
cmd.header = s_nvcmdlist_header[ID];
}
void setFrontFace(GLenum winding){
cmd.frontFace = winding == GL_CCW;
}
};
#pragma pack(pop)
template <class T>
void nvtokenMakeNop(T & token){
NVTokenNop *nop = (NVTokenNop*)&token;
for (size_t i = 0; i < (sizeof(T))/4; i++){
nop[i] = NVTokenNop();
}
}
template <class T>
size_t nvtokenEnqueue(std::string& queue, T& data)
{
size_t offset = queue.size();
std::string cmd = std::string((const char*)&data,sizeof(T));
queue += cmd;
return offset;
}
template <class T>
size_t nvtokenEnqueue(NVPointerStream& queue, T& data)
{
assert(queue.m_cur + sizeof(T) <= queue.m_end);
size_t offset = queue.m_cur - queue.m_begin;
memcpy(queue.m_cur,&data,sizeof(T));
queue.m_cur += sizeof(T);
return offset;
}
//////////////////////////////////////////////////////////
void nvtokenInitInternals( bool hwsupport, bool bindlessSupport);
const char* nvtokenCommandToString( GLenum type );
void nvtokenGetStats( const void* NV_RESTRICT stream, size_t streamSize, int stats[NVTOKEN_TYPES]);
void nvtokenDrawCommandsSW(GLenum mode, const void* NV_RESTRICT stream, size_t streamSize,
const GLintptr* NV_RESTRICT offsets, const GLsizei* NV_RESTRICT sizes,
GLuint count,
StateSystem::State &state);
#if NVTOKEN_STATESYSTEM
void nvtokenDrawCommandsStatesSW(const void* NV_RESTRICT stream, size_t streamSize,
const GLintptr* NV_RESTRICT offsets, const GLsizei* NV_RESTRICT sizes,
const GLuint* NV_RESTRICT states, const GLuint* NV_RESTRICT fbos, GLuint count,
StateSystem &stateSystem);
#endif
}
================================================
FILE: renderer.cpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#include <assert.h>
#include <algorithm>
#include "renderer.hpp"
#include "common.h"
#pragma pack(1)
namespace csfviewer
{
//////////////////////////////////////////////////////////////////////////
bool Renderer::s_bindless_ubo = false;
CullingSystem Renderer::s_cullsys;
ScanSystem Renderer::s_scansys;
const char* toString( enum ShadeType st )
{
switch(st){
case SHADE_SOLID: return "solid";
case SHADE_SOLIDWIRE: return "solid w edges";
case SHADE_SOLIDWIRE_SPLIT: return "solid w edges (split)";
}
return NULL;
}
static void FillCache( std::vector<Renderer::DrawItem>& drawItems, const CadScene::Object& obj, const CadScene::Geometry& geo, bool solid, int objectIndex )
{
int begin = 0;
const CadScene::DrawRangeCache &cache = solid ? obj.cacheSolid : obj.cacheWire;
for (size_t s = 0; s < cache.state.size(); s++)
{
const CadScene::DrawStateInfo &state = cache.state[s];
for (int d = 0; d < cache.stateCount[s]; d++){
// evict
Renderer::DrawItem di;
di.geometryIndex = obj.geometryIndex;
di.matrixIndex = state.matrixIndex;
di.materialIndex = state.materialIndex;
di.objectIndex = objectIndex;
di.solid = solid;
di.range.offset = cache.offsets[begin + d];
di.range.count = cache.counts [begin + d];
drawItems.push_back(di);
}
begin += cache.stateCount[s];
}
}
static void FillJoin( std::vector<Renderer::DrawItem>& drawItems, const CadScene::Object& obj, const CadScene::Geometry& geo, bool solid, int objectIndex )
{
CadScene::DrawRange range;
int lastMaterial = -1;
int lastMatrix = -1;
for (size_t p = 0; p < obj.parts.size(); p++){
const CadScene::ObjectPart& part = obj.parts[p];
const CadScene::GeometryPart& mesh = geo.parts[p];
if (!part.active) continue;
if (part.materialIndex != lastMaterial || part.matrixIndex != lastMatrix){
if (range.count){
// evict
Renderer::DrawItem di;
di.geometryIndex = obj.geometryIndex;
di.matrixIndex = lastMatrix;
di.materialIndex = lastMaterial;
di.objectIndex = objectIndex;
di.solid = solid;
di.range = range;
drawItems.push_back(di);
}
range = CadScene::DrawRange();
lastMaterial = part.materialIndex;
lastMatrix = part.matrixIndex;
}
if (!range.count){
range.offset = solid ? mesh.indexSolid.offset : mesh.indexWire.offset;
}
range.count += solid ? mesh.indexSolid.count : mesh.indexWire.count;
}
// evict
Renderer::DrawItem di;
di.geometryIndex = obj.geometryIndex;
di.matrixIndex = lastMatrix;
di.materialIndex = lastMaterial;
di.objectIndex = objectIndex;
di.solid = solid;
di.range = range;
drawItems.push_back(di);
}
static void FillIndividual( std::vector<Renderer::DrawItem>& drawItems, const CadScene::Object& obj, const CadScene::Geometry& geo, bool solid, int objectIndex )
{
for (size_t p = 0; p < obj.parts.size(); p++){
const CadScene::ObjectPart& part = obj.parts[p];
const CadScene::GeometryPart& mesh = geo.parts[p];
if (!part.active) continue;
Renderer::DrawItem di;
di.geometryIndex = obj.geometryIndex;
di.matrixIndex = part.matrixIndex;
di.materialIndex = part.materialIndex;
di.objectIndex = objectIndex;
di.solid = solid;
di.range = solid ? mesh.indexSolid : mesh.indexWire;
drawItems.push_back(di);
}
}
void Renderer::fillDrawItems( std::vector<DrawItem>& drawItems, size_t from, size_t to, bool solid, bool wire )
{
const CadScene* NV_RESTRICT scene = m_scene;
for (size_t i = from; i < scene->m_objects.size() && i < to; i++){
const CadScene::Object& obj = scene->m_objects[i];
const CadScene::Geometry& geo = scene->m_geometry[obj.geometryIndex];
if (m_strategy == STRATEGY_GROUPS){
if (solid) FillCache(drawItems, obj, geo, true, int(i));
if (wire) FillCache(drawItems, obj, geo, false, int(i));
}
else if (m_strategy == STRATEGY_JOIN) {
if (solid) FillJoin(drawItems, obj, geo, true, int(i));
if (wire) FillJoin(drawItems, obj, geo, false, int(i));
}
else if (m_strategy == STRATEGY_INDIVIDUAL){
if (solid) FillIndividual(drawItems, obj, geo, true, int(i));
if (wire) FillIndividual(drawItems, obj, geo, false, int(i));
}
}
}
}
================================================
FILE: renderer.hpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef RENDERER_H__
#define RENDERER_H__
// bindless UBO
#ifndef GL_UNIFORM_BUFFER_UNIFIED_NV
#define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E
#endif
#ifndef GL_UNIFORM_BUFFER_ADDRESS_NV
#define GL_UNIFORM_BUFFER_ADDRESS_NV 0x936F
#endif
#ifndef GL_UNIFORM_BUFFER_LENGTH_NV
#define GL_UNIFORM_BUFFER_LENGTH_NV 0x9370
#endif
#include "cadscene.hpp"
#include <NvFoundation.h>
#include <nvgl/programmanager_gl.hpp>
#include <nvgl/base_gl.hpp>
#include <nvh/profiler.hpp>
#include "cullingsystem.hpp"
#include "scansystem.hpp"
namespace csfviewer {
#define USE_NOFILTER 0 // some renderers support turning off redundancy filter
#define USE_WIRE_SHADERSWITCH 0 // If set we use two different shaders for tris and lines,
// otherwise we use an immediate mode vertexattrib as pseudo uniform toggle.
// Enable this to stress shader switching in app (becomes primary bottleneck)
enum Strategy {
STRATEGY_GROUPS,
STRATEGY_JOIN,
STRATEGY_INDIVIDUAL,
};
enum ShadeType {
SHADE_SOLID,
SHADE_SOLIDWIRE,
SHADE_SOLIDWIRE_SPLIT, // this mode is not "sane" it is only meant for performance testing of fbo toggles
NUM_SHADES,
};
const char* toString(enum ShadeType st);
struct Resources {
GLuint sceneUbo;
GLuint64 sceneAddr;
GLuint programUbo;
GLuint programUboTris;
GLuint programUboLine;
GLuint programIdx;
GLuint programIdxTris;
GLuint programIdxLine;
GLuint fbo;
GLuint fbo2;
size_t stateChangeID;
size_t fboTextureChangeID;
CullingSystem::View cullView;
// ugly hack
mutable GLuint programUsed;
mutable GLuint programUsedTris;
mutable GLuint programUsedLine;
void usingUboProgram(bool ubo=true) const
{
programUsed = ubo ? programUbo : programIdx;
programUsedTris = ubo ? programUboTris : programIdxTris;
programUsedLine = ubo ? programUboLine : programIdxLine;
}
Resources() {
stateChangeID = 0;
fboTextureChangeID = 0;
}
};
#if USE_WIRE_SHADERSWITCH
#define SetWireMode(state) glUseProgram((state) ? resources.programUsedLine : resources.programUsedTris )
#else
#define SetWireMode(state) glVertexAttribI1i(VERTEX_WIREMODE,(state))
#endif
class Renderer {
public:
struct DrawItem {
bool solid;
int materialIndex;
int geometryIndex;
int matrixIndex;
int objectIndex;
CadScene::DrawRange range;
};
static bool DrawItem_compare_groups(const DrawItem& a, const DrawItem& b)
{
int diff = 0;
diff = diff != 0 ? diff : (a.solid == b.solid ? 0 : ( a.solid ? -1 : 1 ));
diff = diff != 0 ? diff : (a.materialIndex - b.materialIndex);
diff = diff != 0 ? diff : (a.geometryIndex - b.geometryIndex);
diff = diff != 0 ? diff : (a.matrixIndex - b.matrixIndex);
return diff < 0;
}
class Type {
public:
Type() {
getRegistry().push_back(this);
}
public:
virtual bool loadPrograms( nvgl::ProgramManager &mgr ) { return true; }
virtual void updatedPrograms( nvgl::ProgramManager &mgr ) { }
virtual bool isAvailable() const = 0;
virtual const char* name() const = 0;
virtual Renderer* create() const = 0;
virtual unsigned int priority() const { return 0xFF; }
};
typedef std::vector<Type*> Registry;
static bool s_bindless_ubo;
static Registry& getRegistry()
{
static Registry s_registry;
return s_registry;
}
static CullingSystem s_cullsys;
static ScanSystem s_scansys;
public:
virtual void init(const CadScene* NV_RESTRICT scene, const Resources& resources) {}
virtual void deinit() {}
virtual void draw(ShadeType shadetype, const Resources& resources, nvh::Profiler& profiler, nvgl::ProgramManager &progManager ) {}
virtual ~Renderer() {}
void fillDrawItems( std::vector<DrawItem>& drawItems, size_t from, size_t to, bool solid, bool wire);
Strategy m_strategy;
const CadScene* NV_RESTRICT m_scene;
};
}
#endif
================================================
FILE: rendererindexedmdi.cpp
================================================
/*
* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
* SPDX-License-Identifier: Apache-2.0
*/
/* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */
#include <assert.h>
#include <algorithm>
#include "renderer.hpp"
#include "common.h"
#define USE_VERTEX_ASSIGNS (!USE_BASEINSTANCE)
#define USE_GPU_INDIRECT 1
#define USE_CPU_INDIRECT (!USE_GPU_INDIRECT)
namespace csfviewer
{
//////////////////////////////////////////////////////////////////////////
class RendererIndexedMDI: public Renderer {
public:
class Type : public Renderer::Type
{
bool isAvailable() const
{
return true;
}
const char* name() const
{
return "indexedmdi";
}
Renderer* create() const
{
RendererIndexedMDI* renderer = new RendererIndexedMDI();
return renderer;
}
unsigned int priority() const
{
return 3;
}
};
class TypeVbum : public Renderer::Type
{
bool isAvailable() const
{
return !!has_GL_NV_vertex_buffer_unified_memory;
}
const char* name() const
{
return "indexedmdi_bindless";
}
Renderer* create() const
{
RendererIndexedMDI* renderer = new RendererIndexedMDI();
renderer->m_vbum = true;
return renderer;
}
unsigned int priority() const
{
return 3;
}
};
class TypeSort : public Renderer::Type
{
bool isAvailable() const
{
return true;
}
const char* name() const
{
return "indexedmdi_sorted";
}
Renderer* create() const
{
RendererIndexedMDI* renderer = new RendererIndexedMDI();
renderer->m_sort = true;
return renderer;
}
unsigned int priority() const
{
return 3;
}
};
class TypeSortVbum : public Renderer::Type
{
bool isAvailable() const
{
return !!has_GL_NV_vertex_buffer_unified_memory;
}
const char* name() const
{
return "indexedmdi_sorted_bindless";
}
Renderer* create() const
{
RendererIndexedMDI* renderer = new RendererIndexedMDI();
renderer->m_vbum = true;
renderer->m_sort = true;
return renderer;
}
unsigned int priority() const
{
return 3;
}
};
private:
struct DrawIndirectGL {
GLuint count;
GLuint instanceCount;
GLuint firstIndex;
GLint baseVertex;
GLuint baseInstance;
DrawIndirectGL ()
: count(0)
, instanceCount(1)
, firstIndex(0)
, baseVertex(0)
, baseInstance(0) {}
};
struct IndexedCommand {
DrawIndirectGL cmd;
};
struct ShadeCommand {
std::vector<IndexedCommand> indirects;
std::vector<int> assigns;
std::vector<size_t> sizes;
std::vector<size_t> offsets;
std::vector<int> geometries;
std::vector<bool> solids;
#if USE_GPU_INDIRECT
GLuint indirectGL;
GLuint64 indirectADDR;
#endif
#if USE_VERTEX_ASSIGNS
GLuint assignGL;
GLuint64 assignADDR;
#endif
ShadeCommand() {
#if USE_GPU_INDIRECT
indirectGL = 0;
#endif
#if USE_VERTEX_ASSIGNS
assignGL = 0;
#endif
}
};
public:
void init(const CadScene* NV_RESTRICT scene, const Resources& resources);
void deinit();
void draw(ShadeType shadetype, const Resources& resources, nvh::Profiler& profiler, nvgl::ProgramManager &progManager);
bool m_vbum;
bool m_sort;
RendererIndexedMDI()
: m_vbum(false)
, m_sort(false)
{
}
private:
ShadeCommand m_shades[NUM_SHADES];
GLuint packBaseInstance( int matrixIndex, int materialIndex )
{
assert( materialIndex <= 0xFFF );
assert( matrixIndex <= 0xFFFFF );
return (GLuint(matrixIndex) | (GLuint(materialIndex) << 20));
}
void GenerateIndirects(std::vector<DrawItem>& drawItems, ShadeType shade, const CadScene* NV_RESTRICT scene, const Resources& resources )
{
int lastMaterial = -1;
int lastGeometry = -1;
int lastMatrix = -1;
bool lastSolid = true;
ShadeCommand& sc = m_shades[shade];
sc.assigns.clear();
sc.indirects.clear();
sc.sizes.clear();
sc.offsets.clear();
sc.solids.clear();
sc.geometries.clear();
std::vector<int>& assigns = sc.assigns;
std::vector<IndexedCommand>& indirectStream = sc.indirects;
size_t begin = 0;
int numAssigns = 0;
for (int i = 0; i < drawItems.size(); i++){
const DrawItem& di = drawItems[i];
if (shade == SHADE_SOLID && !di.solid){
if (m_sort) break;
continue;
}
if (lastGeometry != di.geometryIndex || (shade == SHADE_SOLIDWIRE && di.solid != lastSolid)){
sc.offsets.push_back( begin );
sc.sizes. push_back( GLsizei((indirectStream.size()-begin)) );
sc.solids. push_back( lastSolid );
sc.geometries.push_back( lastGeometry );
begin = indirectStream.size();
}
#if USE_VERTEX_ASSIGNS
if (lastMatrix != di.matrixIndex || lastMaterial != di.materialIndex)
{
// push indices
assigns.push_back(di.matrixIndex);
assigns.push_back(di.materialIndex);
numAssigns++;
lastMatrix = di.matrixIndex;
lastMaterial = di.materialIndex;
}
#endif
IndexedCommand drawelems;
drawelems.cmd.count = di.range.count;
drawelems.cmd.firstIndex = GLuint((di.range.offset )/sizeof(GLuint));
#if USE_VERTEX_ASSIGNS
drawelems.cmd.baseInstance = numAssigns - 1;
#else
drawelems.cmd.baseInstance = packBaseInstance(di.matrixIndex, di.materialIndex);
#endif
indirectStream.push_back(drawelems);
lastGeometry = di.geometryIndex;
lastSolid = di.solid;
}
sc.offsets.push_back( begin );
sc.sizes. push_back( GLsizei((indirectStream.size()-begin)) );
sc.solids. push_back( lastSolid );
sc.geometries.push_back( lastGeometry );
}
};
static RendererIndexedMDI::Type s_indexed;
static RendererIndexedMDI::TypeVbum s_indexed_vbum;
static RendererIndexedMDI::TypeSort s_indexedsort;
static RendererIndexedMDI::TypeSortVbum s_indexedsort_vbum;
void RendererIndexedMDI::init( const CadScene* NV_RESTRICT scene, const Resources& resources )
{
m_scene = scene;
resources.usingUboProgram(false);
std::vector<DrawItem> drawItems;
fillDrawItems(drawItems,0,scene->m_objects.size(), true, true);
if (m_sort){
std::sort(drawItems.begin(),drawItems.end(),DrawItem_compare_groups);
}
// build SC
GenerateIndirects(drawItems, SHADE_SOLID, scene, resources);
GenerateIndirects(drawItems, SHADE_SOLIDWIRE, scene, resources);
for (size_t i = 0; i <= SHADE_SOLIDWIRE; i++){
ShadeCommand& sc = m_shades[i];
#if USE_GPU_INDIRECT
glCreateBuffers(1,&sc.indirectGL);
glNamedBufferStorage( sc.indirectGL, sizeof(IndexedCommand) * sc.indirects.size(), &sc.indirects[0], 0 );
if (m_vbum){
glGetNamedBufferParameterui64vNV(sc.indirectGL, GL_BUFFER_GPU_ADDRESS_NV, &sc.indirectADDR);
glMakeNamedBufferResidentNV(sc.indirectGL, GL_READ_ONLY);
}
#endif
#if USE_VERTEX_ASSIGNS
glCreateBuffers(1,&sc.assignGL);
glNamedBufferStorage( sc.assignGL, sizeof(int) * sc.assigns.size(), &sc.assigns[0], 0 );
if (m_vbum){
glGetNamedBufferParameterui64vNV(sc.assignGL, GL_BUFFER_GPU_ADDRESS_NV, &sc.assignADDR);
glMakeNamedBufferResidentNV(sc.assignGL, GL_READ_ONLY);
}
#endif
}
m_shades[SHADE_SOLIDWIRE_SPLIT] = m_shades[SHADE_SOLIDWIRE];
}
void RendererIndexedMDI::deinit()
{
for (size_t i = 0; i < SHADE_SOLIDWIRE; i++){
ShadeCommand& sc = m_shades[i];
if (m_vbum){
#if USE_GPU_INDIRECT
glMakeNamedBufferNonResidentNV(sc.indirectGL);
#endif
#if USE_VERTEX_ASSIGNS
glMakeNamedBufferNonResidentNV(sc.assignGL);
#endif
}
#if USE_GPU_INDIRECT
glDeleteBuffers(1,&sc.indirectGL);
#endif
#if USE_VERTEX_ASSIGNS
glDeleteBuffers(1,&sc.assignGL);
#endif
}
}
void RendererIndexedMDI::draw( ShadeType shadetype, const Resources& resources, nvh::Profiler& profiler, nvgl::ProgramManager &progManager )
{
const CadScene* NV_RESTRICT scene = m_scene;
bool vbum = m_vbum;
scene->enableVertexFormat(VERTEX_POS,VERTEX_NORMAL);
glUseProgram(resources.programIdx);
if (shadetype == SHADE_SOLIDWIRE || shadetype == SHADE_SOLIDWIRE_SPLIT){
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1,1);
}
SetWireMode(GL_FALSE);
#if USE_VERTEX_ASSIGNS
glVertexAttribIFormat(VERTEX_ASSIGNS,2,GL_INT,0);
glVertexAttribBinding(VERTEX_ASSIGNS,1);
glEnableVertexAttribArray(VERTEX_ASSIGNS);
glBindVertexBuffer(1,0,0,sizeof(GLint)*2);
glVertexBindingDivisor(1,1);
#endif
if (vbum){
glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
#if USE_GPU_INDIRECT
glEnableClientState(GL_DRAW_INDIRECT_UNIFIED_NV);
#endif
}
if (vbum && s_bindless_ubo){
glEnableClientState(GL_UNIFORM_BUFFER_UNIFIED_NV);
glBufferAddressRangeNV(GL_UNIFORM_BUFFER_ADDRESS_NV, UBO_MATERIAL, scene->m_materialsADDR, sizeof(CadScene::Material) * scene->m_materials.size() );
glBufferAddressRangeNV(GL_UNIFORM_BUFFER_ADDRESS_NV, UBO_SCENE,resources.sceneAddr,sizeof(SceneData));
}
else{
glBindBufferBase(GL_UNIFORM_BUFFER, UBO_SCENE, resources.sceneUbo);
glBindBufferBase(GL_UNIFORM_BUFFER, UBO_MATERIAL, scene->m_materialsGL);
}
nvgl::bindMultiTexture(GL_TEXTURE0 + TEX_MATRICES, GL_TEXTURE_BUFFER, scene->m_matricesTexGL);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
{
ShadeCommand& sc = m_shades[shadetype];
if (vbum){
#if USE_GPU_INDIRECT
glBufferAddressRangeNV(GL_DRAW_INDIRECT_ADDRESS_NV, 0, sc.indirectADDR, sc.indirects.size() * sizeof(IndexedCommand) );
#endif
#if USE_VERTEX_ASSIGNS
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 1, sc.assignADDR, sc.assigns.size() * sizeof(GLint));
#endif
}
else{
#if USE_GPU_INDIRECT
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, sc.indirectGL);
#endif
#if USE_VERTEX_ASSIGNS
glBindVertexBuffer(1, sc.assignGL, 0, sizeof(GLint)*2);
#endif
}
#if USE_CPU_INDIRECT
size_t offset = (size_t)&sc.indirects[0];
#else
size_t offset = 0;
#endif
int lastGeometry = -1;
bool lastSolid = true;
for (size_t i = 0; i < sc.geometries.size(); i++){
int geometryIndex = sc.geometries[i];
if (geometryIndex != lastGeometry){
const CadScene::Geometry& geo = m_scene->m_geometry[ geometryIndex ];
if (vbum){
glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, geo.vboADDR, geo.numVertices * sizeof(CadScene::Vertex));
glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_N
gitextract_wdi1bw94/ ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING ├── LICENSE ├── README.md ├── cadscene.cpp ├── cadscene.hpp ├── common.h ├── csf.cpp ├── csfviewer.cpp ├── cull-bitpack.vert.glsl ├── cull-downsample.frag.glsl ├── cull-downsample.vert.glsl ├── cull-raster.frag.glsl ├── cull-raster.geo.glsl ├── cull-raster.vert.glsl ├── cull-tokencmds.vert.glsl ├── cull-tokensizes.vert.glsl ├── cull-xfb.vert.glsl ├── cullingsystem.cpp ├── cullingsystem.hpp ├── nodetree.cpp ├── nodetree.hpp ├── nvtoken.cpp ├── nvtoken.hpp ├── renderer.cpp ├── renderer.hpp ├── rendererindexedmdi.cpp ├── renderertoken.cpp ├── renderertokensortcull.cpp ├── renderertokenstream.cpp ├── rendereruborange.cpp ├── rendererubosub.cpp ├── scan.comp.glsl ├── scansystem.cpp ├── scansystem.hpp ├── scene.frag.glsl ├── scene.vert.glsl ├── statesystem.cpp ├── statesystem.hpp ├── tokenbase.cpp ├── tokenbase.hpp ├── transform-leaves.comp.glsl ├── transform-level.comp.glsl ├── transformsystem.cpp ├── transformsystem.hpp └── xplode-animation.comp.glsl
SYMBOL INDEX (404 symbols across 24 files)
FILE: cadscene.cpp
function randomVector (line 34) | glm::vec4 randomVector(float from, float to)
function recursiveHierarchy (line 45) | static void recursiveHierarchy(NodeTree& tree, CSFile* csf, int idx, int...
type ListItem (line 460) | struct ListItem
function ListItem_compare (line 466) | static bool ListItem_compare(const ListItem& a, const ListItem& b)
function fillCache (line 476) | static void fillCache(CadScene::DrawRangeCache& cache, const std::vector...
FILE: cadscene.hpp
class CadScene (line 30) | class CadScene {
type BBox (line 34) | struct BBox {
method BBox (line 38) | BBox() : min(FLT_MAX), max(-FLT_MAX) {}
method merge (line 40) | inline void merge( const glm::vec4& point )
method merge (line 46) | inline void merge( const BBox& bbox )
method BBox (line 52) | inline BBox transformed ( const glm::mat4 &matrix, int dim=3)
type MaterialSide (line 88) | struct MaterialSide {
type Material (line 96) | struct Material {
method Material (line 102) | Material() {
type MatrixNode (line 108) | struct MatrixNode {
type Vertex (line 115) | struct Vertex {
type DrawRange (line 120) | struct DrawRange {
method DrawRange (line 124) | DrawRange() : offset(0) , count(0) {}
type DrawStateInfo (line 127) | struct DrawStateInfo {
type DrawRangeCache (line 140) | struct DrawRangeCache {
type GeometryPart (line 148) | struct GeometryPart {
type Geometry (line 153) | struct Geometry {
type ObjectPart (line 170) | struct ObjectPart {
type Object (line 176) | struct Object {
FILE: common.h
type SceneData (line 45) | struct SceneData {
function uniform (line 73) | uniform sceneBuffer {
function uniform (line 78) | uniform matrixBuffer {
function mat4 (line 99) | mat4 getIndexedMatrix(int idx, int what)
FILE: csfviewer.cpp
type csfviewer (line 53) | namespace csfviewer {
class Sample (line 60) | class Sample : public nvgl::AppWindowProfilerGL
type GuiEnums (line 63) | enum GuiEnums
type Tweak (line 111) | struct Tweak
method Sample (line 171) | Sample() { setupConfigParameters(); }
method end (line 183) | void end() override { ImGui::ShutdownGL(); }
method mouse_pos (line 185) | bool mouse_pos(int x, int y) override
method mouse_button (line 191) | bool mouse_button(int button, int action) override
method mouse_wheel (line 197) | bool mouse_wheel(int wheel) override
method key_char (line 203) | bool key_char(int button) override
method key_button (line 209) | bool key_button(int button, int action, int mods) override
function addPath (line 865) | static std::string addPath(std::string const& defaultPath, std::string...
function main (line 930) | int main(int argc, const char** argv)
FILE: cullingsystem.cpp
function minDivide (line 29) | inline unsigned int minDivide(unsigned int val, unsigned int alignment)
FILE: cullingsystem.hpp
class CullingSystem (line 30) | class CullingSystem {
type Programs (line 32) | struct Programs {
type MethodType (line 43) | enum MethodType {
type BitType (line 50) | enum BitType {
type Buffer (line 57) | struct Buffer {
method create (line 63) | void create( size_t sizei, const void* data, GLbitfield flags )
method Buffer (line 72) | Buffer( GLuint buffer, size_t sizei = 0 )
method Buffer (line 88) | Buffer()
method BindBufferRange (line 97) | inline void BindBufferRange(GLenum target, GLuint index) const {
method TexBuffer (line 100) | inline void TexBuffer(GLenum target, GLenum internalformat) const {
method ClearBufferSubData (line 103) | inline void ClearBufferSubData(GLenum target,GLenum internalformat,G...
class Job (line 109) | class Job {
class JobReadback (line 139) | class JobReadback : public Job {
class JobReadbackPersistent (line 155) | class JobReadbackPersistent : public Job {
class JobIndirectUnordered (line 172) | class JobIndirectUnordered : public Job {
type View (line 184) | struct View {
type Uniforms (line 209) | struct Uniforms {
FILE: nodetree.cpp
function clearNode (line 29) | static inline void clearNode(NodeTree::Node &node)
FILE: nodetree.hpp
class NodeTree (line 27) | class NodeTree {
type Flags (line 29) | enum Flags {
type compactID (line 39) | struct compactID {
method compactID (line 43) | compactID(){
type Level (line 52) | struct Level {
method Level (line 57) | Level(){
type Node (line 62) | struct Node {
method getNumUsedLevel (line 90) | inline int getNumUsedLevel() const
method nodeID (line 98) | inline nodeID getTreeRoot()
method Node (line 103) | inline const Node& getNode(nodeID nodeidx) const
method isValid (line 109) | inline bool isValid(unsigned int id)
method isNodeInTree (line 114) | inline bool isNodeInTree(nodeID nodeidx)
method nodeID (line 119) | inline nodeID getParentNode(nodeID nodeidx) const
method getNumActiveNodes (line 140) | int getNumActiveNodes() const {
method Level (line 146) | inline Level& getLevel(int level)
method Level (line 57) | Level(){
method Node (line 154) | inline Node& getNode(nodeID nodeidx)
FILE: nvtoken.cpp
type nvtoken (line 25) | namespace nvtoken
function GLuint (line 36) | static inline GLuint nvtokenHeaderSW(GLuint type, GLuint size){
function GLenum (line 40) | static inline GLenum nvtokenHeaderCommandSW(GLuint header)
function GLuint (line 45) | static inline GLuint nvtokenHeaderSizeSW(GLuint header)
function GLenum (line 50) | static inline GLenum nvtokenHeaderCommand(GLuint header)
function nvtokenRegisterSize (line 61) | static void nvtokenRegisterSize()
function nvtokenInitInternals (line 66) | void nvtokenInitInternals( bool hwsupport, bool bindlessSupport)
function nvtokenGetStats (line 145) | void nvtokenGetStats( const void* NV_RESTRICT stream, size_t streamSiz...
function GLenum (line 163) | static inline GLenum nvtokenDrawCommandSequenceSW( const void* NV_REST...
function nvtokenDrawCommandsSW (line 339) | void nvtokenDrawCommandsSW(GLenum mode, const void* NV_RESTRICT stream...
function nvtokenDrawCommandsStatesSW (line 359) | void nvtokenDrawCommandsStatesSW(const void* NV_RESTRICT stream, size_...
FILE: nvtoken.hpp
type StateSystem (line 36) | namespace StateSystem {
type Faces (line 38) | enum Faces {
type State (line 43) | struct State {
type nvtoken (line 65) | namespace nvtoken
type NVTokenShaderStage (line 74) | enum NVTokenShaderStage {
class NVPointerStream (line 88) | class NVPointerStream {
method init (line 95) | void init(void* data, size_t size)
method size (line 103) | size_t size() const
method capacity (line 108) | size_t capacity() const
type NVTokenSequence (line 114) | struct NVTokenSequence {
type NVTokenNop (line 147) | struct NVTokenNop {
method NVTokenNop (line 152) | NVTokenNop() {
type NVTokenTerminate (line 157) | struct NVTokenTerminate {
method NVTokenTerminate (line 162) | NVTokenTerminate() {
type NVTokenDrawElemsInstanced (line 167) | struct NVTokenDrawElemsInstanced {
method NVTokenDrawElemsInstanced (line 172) | NVTokenDrawElemsInstanced() {
method setMode (line 183) | void setMode(GLenum primmode) {
method setParams (line 187) | void setParams(GLuint count, GLuint firstIndex=0, GLuint baseVertex=0)
method setInstances (line 194) | void setInstances(GLuint count, GLuint baseInstance=0){
type NVTokenDrawArraysInstanced (line 200) | struct NVTokenDrawArraysInstanced {
method NVTokenDrawArraysInstanced (line 205) | NVTokenDrawArraysInstanced() {
method setMode (line 215) | void setMode(GLenum primmode) {
method setParams (line 219) | void setParams(GLuint count, GLuint first=0)
method setInstances (line 225) | void setInstances(GLuint count, GLuint baseInstance=0){
type NVTokenDrawElems (line 231) | struct NVTokenDrawElems {
method NVTokenDrawElems (line 236) | NVTokenDrawElems() {
method setParams (line 244) | void setParams(GLuint count, GLuint firstIndex=0, GLuint baseVertex=0)
method setMode (line 251) | void setMode(GLenum primmode) {
type NVTokenDrawArrays (line 266) | struct NVTokenDrawArrays {
method NVTokenDrawArrays (line 271) | NVTokenDrawArrays() {
method setParams (line 278) | void setParams(GLuint count, GLuint first=0)
method setMode (line 284) | void setMode(GLenum primmode) {
type NVTokenDrawElemsStrip (line 299) | struct NVTokenDrawElemsStrip {
method NVTokenDrawElemsStrip (line 304) | NVTokenDrawElemsStrip() {
method setParams (line 312) | void setParams(GLuint count, GLuint firstIndex=0, GLuint baseVertex=0)
type NVTokenDrawArraysStrip (line 320) | struct NVTokenDrawArraysStrip {
method NVTokenDrawArraysStrip (line 325) | NVTokenDrawArraysStrip() {
method setParams (line 332) | void setParams(GLuint count, GLuint first=0)
type NVTokenVbo (line 339) | struct NVTokenVbo {
method setBinding (line 347) | void setBinding(GLuint idx){
method setBuffer (line 351) | void setBuffer(GLuint buffer, GLuint64 address, GLuint offset)
method NVTokenVbo (line 364) | NVTokenVbo() {
type NVTokenIbo (line 369) | struct NVTokenIbo {
method setType (line 377) | void setType(GLenum type){
method setBuffer (line 392) | void setBuffer(GLuint buffer, GLuint64 address)
method NVTokenIbo (line 404) | NVTokenIbo() {
type NVTokenUbo (line 409) | struct NVTokenUbo {
method setBuffer (line 417) | void setBuffer(GLuint buffer, GLuint64 address, GLuint offset, GLuin...
method setBinding (line 432) | void setBinding(GLuint idx, NVTokenShaderStage stage){
method NVTokenUbo (line 437) | NVTokenUbo() {
type NVTokenBlendColor (line 442) | struct NVTokenBlendColor{
method NVTokenBlendColor (line 447) | NVTokenBlendColor() {
type NVTokenStencilRef (line 452) | struct NVTokenStencilRef{
method NVTokenStencilRef (line 457) | NVTokenStencilRef() {
type NVTokenLineWidth (line 462) | struct NVTokenLineWidth{
method NVTokenLineWidth (line 467) | NVTokenLineWidth() {
type NVTokenPolygonOffset (line 472) | struct NVTokenPolygonOffset{
method NVTokenPolygonOffset (line 477) | NVTokenPolygonOffset() {
type NVTokenAlphaRef (line 482) | struct NVTokenAlphaRef{
method NVTokenAlphaRef (line 487) | NVTokenAlphaRef() {
type NVTokenViewport (line 492) | struct NVTokenViewport{
method NVTokenViewport (line 497) | NVTokenViewport() {
type NVTokenScissor (line 502) | struct NVTokenScissor {
method NVTokenScissor (line 507) | NVTokenScissor() {
type NVTokenFrontFace (line 512) | struct NVTokenFrontFace {
method NVTokenFrontFace (line 517) | NVTokenFrontFace() {
method setFrontFace (line 521) | void setFrontFace(GLenum winding){
function nvtokenMakeNop (line 529) | void nvtokenMakeNop(T & token){
function nvtokenEnqueue (line 537) | size_t nvtokenEnqueue(std::string& queue, T& data)
function nvtokenEnqueue (line 548) | size_t nvtokenEnqueue(NVPointerStream& queue, T& data)
FILE: renderer.cpp
type csfviewer (line 32) | namespace csfviewer
type ShadeType (line 42) | enum ShadeType
function FillCache (line 54) | static void FillCache( std::vector<Renderer::DrawItem>& drawItems, con...
function FillJoin (line 80) | static void FillJoin( std::vector<Renderer::DrawItem>& drawItems, cons...
function FillIndividual (line 135) | static void FillIndividual( std::vector<Renderer::DrawItem>& drawItems...
FILE: renderer.hpp
type csfviewer (line 44) | namespace csfviewer {
type Strategy (line 50) | enum Strategy {
type ShadeType (line 56) | enum ShadeType {
type ShadeType (line 63) | enum ShadeType
type Resources (line 65) | struct Resources {
method usingUboProgram (line 90) | void usingUboProgram(bool ubo=true) const
method Resources (line 97) | Resources() {
class Renderer (line 109) | class Renderer {
type DrawItem (line 112) | struct DrawItem {
method DrawItem_compare_groups (line 121) | static bool DrawItem_compare_groups(const DrawItem& a, const DrawIte...
class Type (line 132) | class Type {
method Type (line 134) | Type() {
method loadPrograms (line 139) | virtual bool loadPrograms( nvgl::ProgramManager &mgr ) { return tr...
method updatedPrograms (line 140) | virtual void updatedPrograms( nvgl::ProgramManager &mgr ) { }
method priority (line 144) | virtual unsigned int priority() const { return 0xFF; }
method Registry (line 150) | static Registry& getRegistry()
method init (line 160) | virtual void init(const CadScene* NV_RESTRICT scene, const Resources...
method deinit (line 161) | virtual void deinit() {}
method draw (line 162) | virtual void draw(ShadeType shadetype, const Resources& resources, n...
FILE: rendererindexedmdi.cpp
type csfviewer (line 33) | namespace csfviewer
class RendererIndexedMDI (line 37) | class RendererIndexedMDI: public Renderer {
class Type (line 39) | class Type : public Renderer::Type
method isAvailable (line 41) | bool isAvailable() const
method Renderer (line 49) | Renderer* create() const
method priority (line 54) | unsigned int priority() const
class TypeVbum (line 59) | class TypeVbum : public Renderer::Type
method isAvailable (line 61) | bool isAvailable() const
method Renderer (line 69) | Renderer* create() const
method priority (line 75) | unsigned int priority() const
class TypeSort (line 80) | class TypeSort : public Renderer::Type
method isAvailable (line 82) | bool isAvailable() const
method Renderer (line 90) | Renderer* create() const
method priority (line 96) | unsigned int priority() const
class TypeSortVbum (line 101) | class TypeSortVbum : public Renderer::Type
method isAvailable (line 103) | bool isAvailable() const
method Renderer (line 111) | Renderer* create() const
method priority (line 118) | unsigned int priority() const
type DrawIndirectGL (line 125) | struct DrawIndirectGL {
method DrawIndirectGL (line 132) | DrawIndirectGL ()
type IndexedCommand (line 140) | struct IndexedCommand {
type ShadeCommand (line 144) | struct ShadeCommand {
method ShadeCommand (line 163) | ShadeCommand() {
method RendererIndexedMDI (line 182) | RendererIndexedMDI()
method GLuint (line 193) | GLuint packBaseInstance( int matrixIndex, int materialIndex )
method GenerateIndirects (line 200) | void GenerateIndirects(std::vector<DrawItem>& drawItems, ShadeType s...
FILE: renderertoken.cpp
type csfviewer (line 27) | namespace csfviewer
class RendererToken (line 31) | class RendererToken: public Renderer, public TokenRendererBase {
class Type (line 33) | class Type : public Renderer::Type
method isAvailable (line 35) | bool isAvailable() const
method Renderer (line 43) | Renderer* create() const
method priority (line 48) | unsigned int priority() const
class TypeAddr (line 53) | class TypeAddr : public Renderer::Type
method isAvailable (line 55) | bool isAvailable() const
method Renderer (line 63) | Renderer* create() const
method priority (line 69) | unsigned int priority() const
class TypeList (line 74) | class TypeList : public Renderer::Type
method isAvailable (line 76) | bool isAvailable() const
method Renderer (line 84) | Renderer* create() const
method priority (line 90) | unsigned int priority() const
class TypeEmu (line 95) | class TypeEmu : public Renderer::Type
method isAvailable (line 97) | bool isAvailable() const
method Renderer (line 105) | Renderer* create() const
method priority (line 111) | unsigned int priority() const
class TypeSort (line 117) | class TypeSort : public Renderer::Type
method isAvailable (line 119) | bool isAvailable() const
method Renderer (line 127) | Renderer* create() const
method priority (line 133) | unsigned int priority() const
class TypeSortAddr (line 138) | class TypeSortAddr : public Renderer::Type
method isAvailable (line 140) | bool isAvailable() const
method Renderer (line 148) | Renderer* create() const
method priority (line 155) | unsigned int priority() const
class TypeSortList (line 160) | class TypeSortList : public Renderer::Type
method isAvailable (line 162) | bool isAvailable() const
method Renderer (line 170) | Renderer* create() const
method priority (line 177) | unsigned int priority() const
class TypeSortEmu (line 182) | class TypeSortEmu : public Renderer::Type
method isAvailable (line 184) | bool isAvailable() const
method Renderer (line 192) | Renderer* create() const
method priority (line 199) | unsigned int priority() const
method GenerateTokens (line 214) | void GenerateTokens(std::vector<DrawItem>& drawItems, ShadeType shad...
FILE: renderertokensortcull.cpp
type csfviewer (line 28) | namespace csfviewer
class RendererCullSortToken (line 36) | class RendererCullSortToken : public Renderer, public TokenRendererBase {
class Shared (line 38) | class Shared {
method Shared (line 45) | static Shared& get()
method Shared (line 51) | Shared() : loaded(false) {}
method load (line 53) | bool load(nvgl::ProgramManager &progManager)
class Type (line 73) | class Type : public Renderer::Type
method isAvailable (line 75) | bool isAvailable() const
method Renderer (line 83) | Renderer* create() const
method loadPrograms (line 88) | bool loadPrograms( nvgl::ProgramManager &mgr)
method priority (line 92) | unsigned int priority() const
class TypeEmu (line 97) | class TypeEmu : public Renderer::Type
method isAvailable (line 99) | bool isAvailable() const
method Renderer (line 107) | Renderer* create() const
method loadPrograms (line 113) | bool loadPrograms( nvgl::ProgramManager &mgr )
method priority (line 117) | unsigned int priority() const
method DrawItem_compare_groups (line 131) | static bool DrawItem_compare_groups(const DrawItem& a, const DrawIte...
type CullSequence (line 145) | struct CullSequence {
type CullShade (line 152) | struct CullShade {
class CullJobToken (line 169) | class CullJobToken : public CullingSystem::Job
method handleToken (line 193) | static void handleToken(std::vector<GLuint> &tokenSizes, std::vector...
method GenerateTokens (line 200) | void GenerateTokens(std::vector<DrawItem>& drawItems, ShadeType shad...
FILE: renderertokenstream.cpp
type csfviewer (line 27) | namespace csfviewer
class RendererTokenStream (line 31) | class RendererTokenStream: public Renderer, public TokenRendererBase {
class Type (line 33) | class Type : public Renderer::Type
method isAvailable (line 35) | bool isAvailable() const
method Renderer (line 43) | Renderer* create() const
method priority (line 48) | unsigned int priority() const
class TypeEmu (line 53) | class TypeEmu : public Renderer::Type
method isAvailable (line 55) | bool isAvailable() const
method Renderer (line 63) | Renderer* create() const
method priority (line 69) | unsigned int priority() const
method GenerateTokens (line 86) | size_t GenerateTokens(NVPointerStream& tokenStream, std::vector<Draw...
FILE: rendereruborange.cpp
type csfviewer (line 29) | namespace csfviewer
class RendererUboRange (line 33) | class RendererUboRange: public Renderer {
class Type (line 35) | class Type : public Renderer::Type
method isAvailable (line 37) | bool isAvailable() const
method Renderer (line 45) | Renderer* create() const
method priority (line 50) | unsigned int priority() const
class TypeEmu (line 55) | class TypeEmu : public Renderer::Type
method isAvailable (line 57) | bool isAvailable() const
method Renderer (line 65) | Renderer* create() const
method priority (line 71) | unsigned int priority() const
class TypeSort (line 76) | class TypeSort : public Renderer::Type
method isAvailable (line 78) | bool isAvailable() const
method Renderer (line 86) | Renderer* create() const
method priority (line 92) | unsigned int priority() const
class TypeSortEmu (line 97) | class TypeSortEmu : public Renderer::Type
method isAvailable (line 99) | bool isAvailable() const
method Renderer (line 107) | Renderer* create() const
method priority (line 114) | unsigned int priority() const
method RendererUboRange (line 125) | RendererUboRange()
FILE: rendererubosub.cpp
type csfviewer (line 29) | namespace csfviewer
class RendererUboSub (line 33) | class RendererUboSub: public Renderer {
class Type (line 35) | class Type : public Renderer::Type
method isAvailable (line 37) | bool isAvailable() const
method Renderer (line 45) | Renderer* create() const
method priority (line 50) | unsigned int priority() const
class TypeVbum (line 55) | class TypeVbum : public Renderer::Type
method isAvailable (line 57) | bool isAvailable() const
method Renderer (line 65) | Renderer* create() const
method priority (line 71) | unsigned int priority() const
class TypeSort (line 76) | class TypeSort : public Renderer::Type
method isAvailable (line 78) | bool isAvailable() const
method Renderer (line 86) | Renderer* create() const
method priority (line 92) | unsigned int priority() const
class TypeSortVbum (line 97) | class TypeSortVbum : public Renderer::Type
method isAvailable (line 99) | bool isAvailable() const
method Renderer (line 107) | Renderer* create() const
method priority (line 114) | unsigned int priority() const
method RendererUboSub (line 135) | RendererUboSub()
FILE: scansystem.cpp
function GLuint (line 26) | inline static GLuint snapdiv(GLuint input, GLuint align)
FILE: scansystem.hpp
class ScanSystem (line 29) | class ScanSystem {
type Programs (line 34) | struct Programs {
type Buffer (line 40) | struct Buffer {
method create (line 45) | void create(size_t sizei, const void* data, GLbitfield flags)
method Buffer (line 53) | Buffer(GLuint buffer)
method Buffer (line 63) | Buffer()
method BindBufferRange (line 71) | inline void BindBufferRange(GLenum target, GLuint index) const {
method BindBufferRange (line 74) | inline void BindBufferRange(GLenum target, GLuint index, GLintptr of...
method GetNamedBufferSubData (line 78) | inline void GetNamedBufferSubData(void* data){
FILE: statesystem.hpp
class StateSystem (line 31) | class StateSystem {
method isBitSet (line 34) | static inline bool isBitSet(GLbitfield bits, GLuint key)
method setBit (line 39) | static inline void setBit(GLbitfield& bits, GLuint key)
method GLbitfield (line 44) | static GLbitfield getBit(GLuint key)
method GLboolean (line 49) | static inline GLboolean setBitState(GLbitfield& bits, GLuint key, GLbo...
type StateBits (line 63) | enum StateBits {
type StateBitsDepr (line 92) | enum StateBitsDepr {
type Faces (line 102) | enum Faces {
type ClipDistanceState (line 110) | struct ClipDistanceState {
method ClipDistanceState (line 113) | ClipDistanceState()
type AlphaStateDepr (line 124) | struct AlphaStateDepr {
method AlphaStateDepr (line 128) | AlphaStateDepr()
type StencilOp (line 140) | struct StencilOp
type StencilFunc (line 146) | struct StencilFunc
type StencilState (line 152) | struct StencilState{
method StencilState (line 156) | StencilState()
type BlendMode (line 170) | struct BlendMode{
type BlendStage (line 175) | struct BlendStage{
type BlendState (line 179) | struct BlendState{
method BlendState (line 185) | BlendState() {
type DepthState (line 201) | struct DepthState {
method DepthState (line 205) | DepthState() {
type LogicState (line 214) | struct LogicState {
method LogicState (line 217) | LogicState() {
type RasterState (line 226) | struct RasterState {
method RasterState (line 237) | RasterState() {
type RasterStateDepr (line 254) | struct RasterStateDepr {
method RasterStateDepr (line 260) | RasterStateDepr() {
type PrimitiveState (line 273) | struct PrimitiveState {
method PrimitiveState (line 278) | PrimitiveState() {
type SampleState (line 290) | struct SampleState {
method SampleState (line 295) | SampleState() {
type Viewport (line 306) | struct Viewport {
type DepthRange (line 312) | struct DepthRange {
type Scissor (line 316) | struct Scissor {
type DepthRangeState (line 343) | struct DepthRangeState {
method DepthRangeState (line 347) | DepthRangeState() {
type ScissorEnableState (line 379) | struct ScissorEnableState {
method ScissorEnableState (line 382) | ScissorEnableState() {
type MaskState (line 392) | struct MaskState {
method MaskState (line 398) | MaskState() {
type FBOState (line 416) | struct FBOState {
method FBOState (line 423) | FBOState() {
method setFbo (line 434) | void setFbo(GLuint fbo){
type VertexEnableState (line 448) | struct VertexEnableState {
method VertexEnableState (line 451) | VertexEnableState() {
type VertexModeType (line 459) | enum VertexModeType {
type VertexFormat (line 466) | struct VertexFormat {
type VertexBinding (line 478) | struct VertexBinding {
type VertexFormatState (line 483) | struct VertexFormatState {
method VertexFormatState (line 487) | VertexFormatState() {
type VertexData (line 507) | struct VertexData {
type VertexImmediateState (line 516) | struct VertexImmediateState {
method VertexImmediateState (line 519) | VertexImmediateState() {
type ProgramState (line 535) | struct ProgramState {
method ProgramState (line 540) | ProgramState() {
type EnableState (line 550) | struct EnableState {
method EnableState (line 553) | EnableState() {
type EnableStateDepr (line 562) | struct EnableStateDepr {
method EnableStateDepr (line 565) | EnableStateDepr() {
type State (line 576) | struct State {
method State (line 611) | State()
type StateDiffKey (line 641) | struct StateDiffKey{
type StateDiff (line 646) | struct StateDiff {
type ContentBits (line 648) | enum ContentBits {
type StateInternal (line 682) | struct StateInternal {
method StateInternal (line 690) | StateInternal() {
FILE: tokenbase.cpp
type csfviewer (line 28) | namespace csfviewer
FILE: tokenbase.hpp
type csfviewer (line 47) | namespace csfviewer
class TokenRendererBase (line 60) | class TokenRendererBase {
type StateType (line 62) | enum StateType {
type ShadeCommand (line 70) | struct ShadeCommand {
method TokenRendererBase (line 83) | TokenRendererBase()
FILE: transformsystem.hpp
class TransformSystem (line 32) | class TransformSystem {
type Programs (line 35) | struct Programs {
type Buffer (line 40) | struct Buffer {
method Buffer (line 45) | Buffer(GLuint buffer, size_t sizei=0)
method Buffer (line 62) | Buffer()
method BindBufferRange (line 70) | inline void BindBufferRange(GLenum target, GLuint index) const {
method TexBuffer (line 73) | inline void TexBuffer(GLenum target, GLenum internalformat) const {
type Textures (line 87) | enum Textures {
Condensed preview — 47 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (371K chars).
[
{
"path": ".gitignore",
"chars": 290,
"preview": ".clang-format\n.editorconfig\n\n#############################\n#Spirv\n#############################\n*.spv\n*.spva\n*.sass\n*.sa"
},
{
"path": "CMakeLists.txt",
"chars": 3322,
"preview": "cmake_minimum_required(VERSION 3.5)\nget_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)\nProject(${PROJNAME"
},
{
"path": "CONTRIBUTING",
"chars": 1400,
"preview": "https://developercertificate.org/\n\nDeveloper Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Found"
},
{
"path": "LICENSE",
"chars": 10173,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 19758,
"preview": "# gl cadscene render techniques\n\nThis sample implements several scene rendering techniques that target mostly static dat"
},
{
"path": "cadscene.cpp",
"chars": 18239,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cadscene.hpp",
"chars": 5597,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "common.h",
"chars": 2531,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "csf.cpp",
"chars": 925,
"preview": "/*\n * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0"
},
{
"path": "csfviewer.cpp",
"chars": 31574,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-bitpack.vert.glsl",
"chars": 1852,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-downsample.frag.glsl",
"chars": 2084,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-downsample.vert.glsl",
"chars": 986,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-raster.frag.glsl",
"chars": 1026,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-raster.geo.glsl",
"chars": 3859,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-raster.vert.glsl",
"chars": 2340,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-tokencmds.vert.glsl",
"chars": 3043,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-tokensizes.vert.glsl",
"chars": 1226,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cull-xfb.vert.glsl",
"chars": 4111,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cullingsystem.cpp",
"chars": 14122,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "cullingsystem.hpp",
"chars": 6185,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "nodetree.cpp",
"chars": 7792,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "nodetree.hpp",
"chars": 3930,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "nvtoken.cpp",
"chars": 14358,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "nvtoken.hpp",
"chars": 13447,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "renderer.cpp",
"chars": 5425,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "renderer.hpp",
"chars": 5070,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "rendererindexedmdi.cpp",
"chars": 13580,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "renderertoken.cpp",
"chars": 13328,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "renderertokensortcull.cpp",
"chars": 24726,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "renderertokenstream.cpp",
"chars": 10651,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "rendereruborange.cpp",
"chars": 8071,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "rendererubosub.cpp",
"chars": 7642,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "scan.comp.glsl",
"chars": 6573,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "scansystem.cpp",
"chars": 6310,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "scansystem.hpp",
"chars": 2796,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "scene.frag.glsl",
"chars": 2217,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "scene.vert.glsl",
"chars": 2041,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "statesystem.cpp",
"chars": 28553,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "statesystem.hpp",
"chars": 16212,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "tokenbase.cpp",
"chars": 9048,
"preview": "/*\n * Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "tokenbase.hpp",
"chars": 3455,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "transform-leaves.comp.glsl",
"chars": 3110,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "transform-level.comp.glsl",
"chars": 2760,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "transformsystem.cpp",
"chars": 5011,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "transformsystem.hpp",
"chars": 2474,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
},
{
"path": "xplode-animation.comp.glsl",
"chars": 2759,
"preview": "/*\n * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved.\n *\n * Licensed under the Apache License, Versio"
}
]
About this extraction
This page contains the full source code of the nvpro-samples/gl_cadscene_rendertechniques GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 47 files (347.6 KB), approximately 94.0k tokens, and a symbol index with 404 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.