Add Vulkan rendering backend, with HDR10 output and raytraced shadows (#7554)

* Shadow overhaul round 2: PCSS, dynamic cascades, cockpit/instanced shadows

Squashed snapshot of lafiel/shadow_overhaul_2's continuation work
(d3922aa87..010f9d2d9), which has no separate upstream PR of its own.

* Add Vulkan rendering backend

Squashed snapshot of the Vulkan backend as vendored into this branch
(ce2d1e85f..6ce539463, authored by Mara van der Laan with build/CI
fixes by Taylor Richards). This work has its own upstream PR #7553
(notimaginative:vulkan-pr-new) and is not otherwise original to true-hdr.

* Add true HDR output, RT shadows, and PCSS shadows for Vulkan

Squashed snapshot of true-hdr's own integration work: true HDR
framebuffer output, wiring the shadow overhaul into the Vulkan
backend (raytraced + cascaded shadow maps), logging refactors, log
filter UI, and CI/build fixes.

* refactor: update Vulkan logging calls to use `nprintf` for scoped logging

* fix: correct shadow cascade count in Vulkan logging calls

* fix: fall back to PATH-resolved ccache when CCACHE_PATH is stale

CI hardcodes /usr/local/bin/ccache, but macOS runners on Apple Silicon
hosts install Homebrew (and ccache) under /opt/homebrew instead,
causing all four Mac configs to fail with "No such file or directory".

* Fix vulkan texture readback to correctly handle transparent pipelines

* Put the pixel swizzles into a util file

* OOB safety check

* Fix fog handling

* Fix MSAA in Vulkan

* fix: enable SMAA for vulkan

* adapt the post-processing AA shaders to hdr

* fix clang-tidy warnings: concatenate nested namespaces, static methods

pixel_swizzle.{h,cpp}: collapse `namespace graphics { namespace util {`
into `namespace graphics::util {` (modernize-concat-nested-namespaces).

VulkanState.h/VulkanDraw.h: getClearColor()/setClearColor() only touch
gr_screen and other globals, no instance state, so make them static
(readability-convert-member-functions-to-static) and drop the
top-level const on getClearColor()'s return type
(readability-const-return-type).

* fix clang-tidy warning

* Extend raytraced shadows to all directional lights

RT shadows previously only shadowed the first directional light in the
scene, even though multi-sun missions push more than one directional
light into the engine. The TLAS raytraced shadows query against is
already light-agnostic, so this extends both the deferred lighting
pass and the forward MODEL shader to shadow every directional light
(capped by a new Max Raytraced Shadow Lights option, configurable via
the in-game Options menu or the Lab's Scene rendering options).

Cascaded shadow maps are untouched: they're built for a single light
only, so the deferred pass still limits itself to one shadowed
directional light whenever CSM (not raytracing) is the active method.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016DA6JHJ3twz2oHGQcZ1Udc

* refactor: improve shader flag handling and render target finalization in Vulkan backend

- Consolidate shader flag setup logic in VulkanPostProcessingLighting to simplify configuration.
- Fix dead field usage in VulkanDraw by ensuring correct image view selection for environment maps.
- Add mip chain regeneration in VulkanRenderer::endRenderTarget to ensure correct mip levels for cubemaps.

* fix: ensure correct mip chain initialization in cubemap rendering

- Add mip chain generation to VulkanDraw for cubemaps with multiple mip levels.
- Fix potential issue with uninitialized mip levels above level 0 in deferred-f.sdr sampling paths.

* fix: don't crash on startup when environment locale is unavailable

std::locale("") pulls in the process's environment locale categories
(LC_CTYPE, LC_MONETARY, LC_PAPER, etc.), and throws std::runtime_error
if any of them names a locale not installed on the host (e.g.
mismatched regional-format overrides). Since SCP_default_locale is
constructed during static initialization, before main() and before any
of our own exception handling exists, an uncaught throw here aborted
the whole process before the game even started. Fall back to the
classic "C" locale instead, which is sufficient since this locale is
only ever used for ASCII case-folding (SCP_toupper/tolower/isdigit).

Co-Authored-By: Claude Sonnet 5 <[email protected]>

* `feat: add raytraced shadows for local lights and optimize TLAS management`

* Make the Commandline authoritative for -res and -vulkan / -opengl

* address BMagnu's comments on the original PR

* update prebuilt version hash to 62c89ef6

* refactor: standardize graphics API handling and document Vulkan limitations in qtFRED

- Updated `AboutDialogModel.cpp` to use `GraphicsAPI` enum for better clarity.
- Ensured qtFRED falls back to OpenGL when Vulkan is requested.
- Added detailed documentation in `qtfred/README.md` regarding Vulkan backend limitations and requirements.

* chore: include cmdline library in main and mission management modules

* docs: add known issue for blank render viewport under Wayland in README.md

* fix CI issues

* chore: add `GR_DEBUG_SCOPE` markers across Vulkan post-processing and rendering paths for improved debugging

* feat: improve Vulkan HDR pipeline with separate encode paths for SDR and HDR10

- Added an HDR10-specific output encode pass in `VulkanRenderer` and `VulkanPostProcessor` that applies PQ/BT.2020 transfer.
- Introduced an optimized SDR fallback path with direct blit support for devices that can skip shader draws.
- Updated `m_sceneLdrCompressed` to provide [0,1]-clamped proxies for HDR edge detection.
- Refactored `copyImageToImage` and added `blitImageToImage` to allow format conversions and optimized transfers.
- Replaced `hdr_mode` with explicit HDR10 flags and data structures for clearer state handling.

* refactor: encapsulate SMAA processing and refactor VulkanPostProcessor

- Moved SMAA-specific logic into `VulkanSMAA` class for better separation of concerns.
- Simplified `initSMAA`, `shutdownSMAA`, and `executeSMAA` logic by leveraging `VulkanLDR` and `PostProcessContext` abstractions.
- Improved initialization and cleanup code with clearer resource lifecycle management.
- Updated debug markers and messages to reference `VulkanSMAA`.
- Enhanced flexibility of fullscreen triangle rendering API with additional parameters (`sampleCount`, `bindGlobalSet`).

* Update Vulkan porting credits in AboutDialogModel

* fix CI issues again

* fix: correct Y-flip in viewport setup for Vulkan light volumes

- Adjusted viewport height to negative value to account for G-buffer render pass's negative Y-axis convention.
- Ensured proper alignment of point and tube light volumes with actual light positions.

* feat: add user-adjustable gamma correction to SDR and HDR10 output paths

- Added gamma correction to HDR10 path, applying user settings before PQ/BT.2020 encode.
- Replaced the SDR blit-only path with an encode pass that applies gamma correction, ensuring consistent behavior across devices.
- Refactored VulkanPostProcessor and associated shaders to support gamma slider adjustments for both SDR and HDR output.

* fix: ensure proper uniform and cull mode setup for Vulkan sphere rendering

- Added uniform buffer setup and default material uniforms in `vulkan_draw_sphere`.
- Disabled face culling in material definition for sphere rendering to align behavior with OpenGL.

* Ensure memory flushes after UBO updates and add explicit pipeline barrier for MoltenVK compatibility.

* fix: add clear pass for SMAA edge detection to prevent persistent artifacts

* feat: add Vulkan debug-utils messenger, opt-in sync validation, frame diagnostics

Groundwork for the renderer-review fix series (.agents/plans/
vulkan-renderer-review-fixes.md, Phase 0):

- Register a VK_EXT_debug_utils messenger (preferred) instead of only the
  deprecated VK_EXT_debug_report callback, which is kept as a fallback for
  loaders without debug-utils. The messenger config is also chained into
  instance creation so vkCreateInstance-time messages are captured.
  Validation errors/warnings now go to the main log via mprintf.
- New -gr_sync_validation cmdline flag (implies -gr_debug): chains
  VkValidationFeaturesEXT with synchronization validation into instance
  creation when VK_LAYER_KHRONOS_validation is available. This is the
  regression net for the synchronization fixes in the following commits.
- Frame stats now report descriptor sets/writes allocated per frame and the
  total pipeline count (descriptor-manager counters; DescriptorWriter::flush
  moved out-of-line to report into the manager).

Co-Authored-By: Claude Fable 5 <[email protected]>

* refactor: unify Vulkan render-pass begins, clamp in-pass clears, add per-frame UBO ring

Phase 1 of the renderer-review fix series: shared helpers that the
correctness fixes build on.

- New VulkanRenderer::beginTrackedRenderPass(PassBeginDesc): every render-pass
  begin on the frame command buffer now records vkCmdBeginRenderPass and
  updates the state tracker (pass, color attachment count, sample count,
  render area, viewport) in one place. Converted: setupFrame,
  beginSceneRendering (both branches), resumeSceneRendering,
  endSceneRendering, beginRenderTarget (viewport kept for gr_set_viewport),
  resumeSwapChainPass, plus new resumeScenePassAfterCopy() shared by
  copyEffectTexture/copySceneDepthForParticles and
  rebeginSwapChainPassAfterReadback() shared by both readbackFramebuffer
  exits (~200 lines of duplicated begin/clear/viewport blocks removed).
- The state tracker records the active render area; all four
  vkCmdClearAttachments sites clamp their rects against it and skip empty
  results. Previously gr_screen-sized clear rects could exceed a smaller
  off-screen render target's render area, which is invalid API usage.
- New PerFrameUboRing utility (VulkanPerFrameUbo.h/.cpp): a persistently
  mapped uniform buffer with one slot region per frame in flight and a
  self-flushing alloc(). Used by the next commit to fix the cross-frame
  UBO races.

Deliberate behavior deltas (all safe-direction): the helper always sets
colorAttachmentCount/sampleCount (some sites previously inherited stale
values), and resumeSwapChainPass/resumeSceneRendering now also reset the
standard flipped viewport instead of leaving the previous one.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix: eliminate Vulkan cross-frame UBO/buffer races, make gr_sync honest

Phase 2 of the renderer-review fix series: with two frames in flight, several
host-visible resources were rewritten by the CPU while the previous frame's
GPU work could still be reading them.

- The post-processing scratch UBO is now a PerFrameUboRing (per-frame-in-
  flight slot regions, persistently mapped, self-flushing alloc). All the
  per-pass map/unmap/cursor bookkeeping in bloom/LDR/fog/SMAA is deleted;
  the cursor resets exactly once per frame via the new
  VulkanPostProcessor::beginFrame() hook in setupFrame.
  This also fixes two latent bugs: (a) fog consumed scratch slots mid-scene
  and bloom's later same-frame cursor reset overwrote them before submission
  whenever fog+bloom were both active; (b) volumetric_fog_data (288 bytes)
  overflowed the 256-byte slots - the memcpy spilled into the neighboring
  slot and the shader's bound range ended before the aspect/fov/noise
  fields. Slots are now 512 bytes (static_assert at the fog call site) and
  the per-frame budget is 32 slots.
- The dedicated tonemap, output-encode, and MSAA-resolve UBOs (each a single
  instance rewritten per frame, i.e. racy; the MSAA one also never flushed)
  are deleted entirely - those passes build their structs on the stack and
  allocate from the scratch ring.
- Static-buffer rewrites orphan instead of racing: getVkBuffer() stamps a
  last-used frame number, and a full-content updateBufferData() on a Static
  buffer used within the last MAX_FRAMES_IN_FLIGHT frames defers the old
  VkBuffer to the deletion queue and writes a fresh one. Offset writes
  (GPU-heap appends to fresh regions) and PersistentMapping buffers (the
  engine holds their mapped pointer and syncs via gr_sync) are exempt.
- gr_sync_wait now honors its contract: VulkanRenderFrame::waitForFinish
  takes a timeout and reports expiry without consuming the fence;
  VulkanRenderer::waitForFrame returns false for fences taken during the
  still-recording frame (previously it waited on a stale or idle slot and
  reported success) and resolves the frame->slot mapping correctly. This
  makes UniformBufferManager's segment-fence throttling actually protective
  on Vulkan for the first time.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Refactor framebuffer and target lifecycle: centralized creation/resizing logic, improved resource management across Vulkan post-processing subsystems.

* Improve Vulkan cross-frame sync with explicit access masks and validation updates

- Unified Vulkan validation flag handling in setup, separating engine debug output from synchronization validation.
- Specified access masks and stage flags to address cross-frame WRITE_AFTER_WRITE vulnerabilities, notably in G-buffer, post-processing, and MSAA resolve paths.
- Adjusted subpass dependencies and image layout transitions to prevent read-after-write and write-after-write hazards, detected using `-gr_sync_validation`.
- Fixed uniform buffer and barrier transitions to ensure proper ordering in render passes across frames.

* Refactor Vulkan render-pass dependencies and sync stages for clarity and future testing

- Clarified dependency logic for cross-pass and cross-frame synchronization, ensuring explicit stage and access flag comments match observed behavior.
- Adjusted subpass dependencies to decouple render-pass compatibility definitions from dependency assumptions.
- Highlighted MoltenVK-specific issues and retained workarounds with notes for re-testing on macOS hardware.
- Updated synchronization barriers and validation notes to address historical inconsistencies across platforms.

* Remove transfer queue support; consolidate all operations into the graphics queue

- Removed transfer queue handling, simplifying buffer and queue logic to use the graphics queue for all transfer operations.
- Updated buffer sharing modes and removed related code paths and parameters for transfer queue families.
- Introduced exclusive buffer access, noting async transfer and queue ownership transfers are future work.
- Limited enabled device features to only those actively used by the renderer, reducing unnecessary feature requests (e.g., robustBufferAccess).

* Refactor frame advancement and descriptor management in VulkanRenderer

- Removed `endFrame()` from `VulkanDescriptorManager`, replacing it with `setCurrentFrame(frameIndex)` for explicit frame synchronization.
- Centralized frame index advancement within `VulkanRenderer` to ensure consistency across components.
- Invalidated cached descriptor set bindings at render-pass boundaries to handle raw recorder staleness correctly.
- Optimized pipeline descriptor rebind logic by removing redundant clears during pipeline changes.

* Refactor `PipelineConfig::hash` and Global descriptor set management for improved hashing and memoization efficiency

- Replaced inefficient shift-based hashing in `PipelineConfig::hash` with a robust `hashCombine` strategy to minimize bucket collisions.
- Introduced `maskBits` helper to streamline bitmask creation for boolean fields.
- Implemented memoization for Global (Set 0) descriptor sets, reducing redundant rebuilds across frames and improving performance.
- Added dirty state tracking (`m_globalSetDirty`) and invalidation logic for Global set updates following relevant input changes (e.g., TLAS or UBO modifications).

* Refactor Vulkan post-processing output logic: extracted shared `encodeToSwapChainPass` method to streamline HDR and SDR output paths.

* Optimize Vulkan descriptor set management with per-frame memoization

- Added memoization for Material (Set 1) and PerDraw (Set 2) descriptor sets to reuse cached sets across frames if inputs remain unchanged.
- Implemented precise input tracking for Material and PerDraw descriptor sets to detect and rebuild only when necessary.
- Reduced redundant Vulkan descriptor allocations and writes, improving performance on repeated or batched draw calls.

* Fix Vulkan access masks for G-buffer and scene pass transitions

- Added `eColorAttachmentRead` to access masks in multiple barriers to ensure proper synchronization for load operations (e.g., `loadOp=eLoad`) across render passes.
- Prevented `READ_AFTER_WRITE` synchronization validation issues in deferred and multi-pass rendering scenarios.
- Updated comments to clarify the reasoning behind the access mask changes and load dependencies.

* Refactor Vulkan resource management and indexed draw logic

- Consolidated destruction queue methods in `VulkanDeletionQueue` using a shared `queueHandle` template to reduce redundancy.
- Introduced `ModelDrawParams` structure and `computeModelDrawParams` function to unify indexed draw parameters for models and shadows.
- Simplified draw logic by eliminating repeated calculation of indexed draw parameters and improving code reuse.

* Add `releaseAnimationSlotRef` for ref-count handling of shared animation texture arrays

- Introduced `releaseAnimationSlotRef` to manage reference counting for shared animation texture-array slots, preventing premature destruction of shared resources.
- Updated `flushTextures` and related methods to integrate `releaseAnimationSlotRef` for shared slot cleanup.
- Refactored staging buffer creation into `createStagingBuffer` for 3D texture uploads and shared path consistency.
- Enhanced texture upload logic with improved tracking and logging for re-upload cases, aiding preloading gap analysis.
- Added instrumentation for on-demand mid-frame texture uploads in `FrameStats`.

* Refactor Vulkan resource and texture management, extend debug logging

- Updated smart pointer usage for consistency across Vulkan resource managers (`std::make_unique`).
- Removed redundant `cubeImageView` member and related cleanup logic; unified sampling view management.
- Added per-instance debug log counters (avoiding static state) for Vulkan texture uploads and bindings, improving visibility during initialization and run-time.
- Optimized `VulkanRenderer` property/feature getters by caching physical device properties and features during device creation.
- Improved memory region flushing logic for out-of-date offsets, aligning with Vulkan Memory Allocator behavior.
- Refactored render pass state tracking to reapply dynamic state reliably after mid-frame render passes.
- Enhanced validation and error handling for mismatched update texture formats.

* fix CI build warning

* Fix on-demand YUV texture locking and set alpha uniform for movie rendering in Vulkan

- Corrected bitmap lock type from `BMP_TEX_OTHER` to `BMP_AABITMAP` for YUV textures during on-demand loading.
- Updated movie rendering to bind alpha from the `MovieData` UBO (set 2, binding 4) for Vulkan, aligning behavior with OpenGL implementation.

* revert inadvertent changes introduced by a squash commit

* address ci issues

* remove wip stuff from code comments that make no sense to people who aren't me

* Remove unused LD_LIBRARY_PATH export and fix shader shadow map sampling.

* Refactor batched rendering uniform setup in Vulkan

- Removed unused fields from `genericData` uniform block, simplifying shader definitions.
- Updated Vulkan material uniform handling to align with OpenGL's batched rendering logic.
- Introduced `batched_data` struct for streamlined UBO updates, improving maintainability and clarity.

* Add `-hdr` command-line option for HDR nits configuration

- Introduced `-hdr` parameter to set paper white and peak luminance in nits, overriding in-game HDR settings.
- Added input validation with error feedback for incorrect format.

* Remove unused `MODEL_SDR_FLAG_SHADOW_MAP` macro from shader flags

* clean up unrelated changes

* Invalidate Vulkan state tracker bindings after external command buffer modifications

- Added `invalidateExternalBindings()` to `VulkanStateTracker` to handle stale pipelines, descriptors, and dynamic states caused by external Vulkan commands (e.g., ImGui renders mid-pass).
- Updated ImGui rendering flow to call `invalidateExternalBindings()` post-draw, ensuring consistent pipeline state for subsequent engine renders.
- Adjusted image usage flags to account for `eTransferSrc` in texture readbacks.
- Fixed barrier `dstAccessMask` and pipeline stage flags to ensure proper synchronization for image transitions.

* Add a few more generically useful lines to the log

* Simplify Qt deployment logic and improve handling for system-installed Qt on Linux

* add cmdline headers to make qtFred compile

* use VK_KHR_synchronization2 for sync and barrier purposes

* Simplify and clean up Qt deployment logic in CMake for qtfred

* update get_bitmap_from_texture to match the new API

* Enhance `-hdr` command-line option to force HDR output and support optional nits configuration with improved input validation and overrides.

* Improve Vulkan robustness, add device feature checks, and refine memory/logging.

- Added depth clamping support gated by device feature checks, with fallback where unsupported.
- Improved deferred lighting UBO overflow handling with user-accessible warnings and buffer size adjustments.
- Enhanced Vulkan logging for validation, allocation, and debugging:
  - Automatic logging for descriptor pool exhaustion/fragmentation.
  - Vertex format and resource destruction checks.
  - Notifications for generic layout transitions and volumetric texture uploads.
- Consolidated shader logic to ensure Vulkan and OpenGL parity.

* Remove HDR10 encode shader and consolidate gamma correction logic for SDR and HDR10. Replace `Verify` assertions with `Assert`

* Add braces to conditional in object lighting logic for clarity

* Refine Vulkan memory allocation checks with `Verification` for improved error handling and messaging.

* Refine VulkanBuffer resize logic to preserve contents with CPU-side copy and add safeguards for non-coherent memory scenarios.

* Add detailed comments explaining descriptor array capacity constants in VulkanDescriptorManager

* Gate shadow support on depth clamp feature availability and clarify its necessity with improved comments.

---------

Co-authored-by: BMagnu <[email protected]>
Co-authored-by: Mara van der Laan <[email protected]>
Co-authored-by: Claude <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-07-21 16:58:21 +00:00
1 parent 3e317d9734
commit b26b9cf743
228 files changed
+48749 -2973

No files matched your search

+1 -1
View File
@@ -134,7 +134,7 @@ OPTION(FSO_BUILD_WITH_OPENGL "Enable compilation of the OpenGL renderer" ON)
OPTION(FSO_BUILD_WITH_OPENGL_DEBUG "Enables debug option for OpenGL" OFF)
OPTION(FSO_BUILD_WITH_OPENGL_ES "When building OpenGL, use OpenGL ES compatibility layer (External shaders only!)" OFF)
OPTION(FSO_BUILD_WITH_VULKAN "Enable compilation of the Vulkan renderer" OFF)
OPTION(FSO_BUILD_WITH_VULKAN "Enable compilation of the Vulkan renderer" ON)
if(NOT FSO_BUILD_WITH_OPENGL AND FSO_BUILD_WITH_OPENGL_ES)
# Disable GLES if not building with OpenGL
-5
View File
@@ -18,13 +18,8 @@ fi
# branch
BASE_COMMIT=$(git merge-base $1 $2)
# Note: Manually passing in the Vulkan flags that are normally provided by cmake (but are not so, here), to ensure
# that the source files are checked with the actual configuration used.
echo "Running clang-tidy on changed files"
git diff -U0 --no-color "$BASE_COMMIT..$2" | \
$HERE/clang-tidy-diff.py -path "$(pwd)/build" -p1 \
-extra-arg="-DWITH_VULKAN" \
-extra-arg="-DVULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1" \
-extra-arg="-DVK_NO_PROTOTYPES" \
-regex '(code(?!((\/graphics\/shaders\/compiled)|(\/globalincs\/windebug)|(\/def_files\/data)))|freespace2|qtfred|test\/src|build|tools)\/.*\.(cpp|h)' \
-clang-tidy-binary /usr/bin/clang-tidy-16 -j$(nproc) -export-fixes "$(pwd)/clang-fixes.yaml"
+9 -11
View File
@@ -17,11 +17,6 @@ if [ "$RUNNER_OS" = "macOS" ]; then
CXXFLAGS="-mtune=generic -pipe -Wno-unknown-pragmas"
CFLAGS="-mtune=generic -pipe -Wno-unknown-pragmas"
export CMAKE_OSX_ARCHITECTURES="$ARCHITECTURE"
# the ccache-action should install via homebrew, which means that we can't
# hardcode the correct path in the workflow and must override it instead
if [ ! "$CCACHE_PATH" = "" ]; then
CCACHE_PATH="$(brew --prefix)/bin/ccache"
fi
else
PLATFORM_CMAKE_OPTIONS="-DFSO_BUILD_APPIMAGE=ON -DFORCED_SIMD_INSTRUCTIONS=SSE2"
@@ -39,13 +34,16 @@ if [[ "$COMPILER" =~ ^clang.*$ ]]; then
CMAKE_OPTIONS="$CMAKE_OPTIONS -DCLANG_USE_LIBCXX=${CLANG_USE_LIBCXX:-OFF}"
fi
if [ ! "$CCACHE_PATH" = "" ] && [ ! -x "$CCACHE_PATH" ]; then
# The configured path doesn't exist on this runner (e.g. macOS runners on Apple
# Silicon hosts install Homebrew under /opt/homebrew instead of /usr/local),
# so fall back to whatever ccache is actually on PATH.
CCACHE_PATH="$(command -v ccache || true)"
fi
if [ ! "$CCACHE_PATH" = "" ]; then
if [ -x "$CCACHE_PATH" ]; then
echo "Using ccache at $CCACHE_PATH"
CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_C_COMPILER_LAUNCHER=$CCACHE_PATH -DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_PATH"
else
echo "Invalid or missing ccache binary: $CCACHE_PATH"
fi
echo "Using ccache at $CCACHE_PATH"
CMAKE_OPTIONS="$CMAKE_OPTIONS -DCMAKE_C_COMPILER_LAUNCHER=$CCACHE_PATH -DCMAKE_CXX_COMPILER_LAUNCHER=$CCACHE_PATH"
fi
if [ -n "${ENABLE_QTFRED:-}" ]; then
+23 -19
View File
@@ -66,33 +66,37 @@ ENDIF(EXISTS \"${CMAKE_CURRENT_BINARY_DIR}/${TARGET}/${FILE}\")
SET(${OUTVAR} "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}/${NAME} PARENT_SCOPE)
ENDFUNCTION(EP_CHECK_FILE_EXISTS)
# Use rsync if possible for COPY_FILE_TO_TARGET macro in order to preserve
# symlinks as copy_if_different follows them which results in multiple copies
if(PLATFORM_UNIX AND NOT DEFINED RSYNC_BIN)
find_program(RSYNC_BIN rsync)
endif()
MACRO(COPY_FILE_TO_TARGET _target _file)
if (IS_DIRECTORY "${_file}")
get_filename_component(_dirName "${_file}" NAME)
if (PLATFORM_MAC AND ("${_file}" MATCHES ".framework$"))
# This is stupid, but it preserves symlinks, unlike copy_directory_if_different.
# Otherwise we end up creating duplicate files in the copied framework.
ADD_CUSTOM_COMMAND(
TARGET ${_target} POST_BUILD
COMMAND rsync -rlq "${_file}" "$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}"
COMMENT "copying '${_file}'..."
VERBATIM
)
else()
if(RSYNC_BIN)
ADD_CUSTOM_COMMAND(
TARGET ${_target} POST_BUILD
COMMAND ${RSYNC_BIN} -rlq "${_file}" "$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}"
COMMENT "copying '${_file}'..."
VERBATIM
)
else()
if (IS_DIRECTORY "${_file}")
get_filename_component(_dirName "${_file}" NAME)
ADD_CUSTOM_COMMAND(
TARGET ${_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different "${_file}" "$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}/${_dirName}"
COMMENT "copying '${_file}'..."
VERBATIM
)
else()
ADD_CUSTOM_COMMAND(
TARGET ${_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_file}" "$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}/"
COMMENT "copying '${_file}'..."
VERBATIM
)
endif()
else()
ADD_CUSTOM_COMMAND(
TARGET ${_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_file}" "$<TARGET_FILE_DIR:${_target}>/${LIBRAY_DESTINATION}/"
COMMENT "copying '${_file}'..."
VERBATIM
)
endif()
endmacro(COPY_FILE_TO_TARGET)
+3 -10
View File
@@ -113,16 +113,9 @@ if (FSO_BUILD_WITH_OPENGL)
target_compile_definitions(code PUBLIC WITH_OPENGL)
endif()
if (FSO_BUILD_WITH_VULKAN)
find_package(Vulkan REQUIRED)
if (Vulkan_FOUND)
target_compile_definitions(code PUBLIC WITH_VULKAN)
target_link_libraries(code PRIVATE Vulkan::Vulkan)
target_compile_definitions(code PUBLIC WITH_VULKAN VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 VK_NO_PROTOTYPES)
else()
message(WARNING "FSO_BUILD_WITH_VULKAN was set, but the package was unable to be found. Forcing OFF.")
set(FSO_BUILD_WITH_VULKAN OFF CACHE BOOL "Enable compilation of the Vulkan renderer" FORCE)
endif()
target_compile_definitions(code PUBLIC WITH_VULKAN VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 VK_NO_PROTOTYPES)
target_include_directories(code PUBLIC Vulkan::Headers)
target_link_libraries(code PUBLIC VulkanMemoryAllocator)
endif()
include(shaders.cmake)
+44 -2
View File
@@ -356,6 +356,7 @@ cmdline_parm fb_explosions_arg("-fb_explosions", NULL, AT_NONE);
cmdline_parm fb_thrusters_arg("-fb_thrusters", NULL, AT_NONE);
cmdline_parm shadow_quality_arg("-shadow_quality", NULL, AT_INT);
cmdline_parm enable_shadows_arg("-enable_shadows", NULL, AT_NONE);
cmdline_parm rt_shadows_arg("-rt_shadows", nullptr, AT_NONE);
cmdline_parm no_deferred_lighting_arg("-no_deferred", NULL, AT_NONE); // Cmdline_no_deferred
cmdline_parm deferred_lighting_cockpit_arg("-deferred_cockpit", nullptr, AT_NONE);
cmdline_parm anisotropy_level_arg("-anisotropic_filter", NULL, AT_INT);
@@ -513,6 +514,7 @@ cmdline_parm deprecated_res_arg("-res", "Deprecated, resolution, formatted like
cmdline_parm render_res_arg("-render_res", "Resolution, formatted like 1600x900", AT_STRING);
cmdline_parm window_res_arg("-window_res", "Window resolution, formatted like 1600x900.", AT_STRING);
cmdline_parm center_res_arg("-center_res", "Resolution of center monitor, formatted like 1600x900", AT_STRING);
cmdline_parm hdr_nits_arg("-hdr", "Forces HDR output on, overriding the in-game HDR setting. Optionally takes paper white and peak luminance in nits, formatted like 400,1500, overriding the in-game HDR nits settings.", AT_STRING);
cmdline_parm verify_vps_arg("-verify_vps", NULL, AT_NONE); // Cmdline_verify_vps -- spew VP crcs to vp_crcs.txt
cmdline_parm parse_cmdline_only(PARSE_COMMAND_LINE_STRING, "Ignore any cmdline_fso.cfg files", AT_NONE);
cmdline_parm reparse_mainhall_arg("-reparse_mainhall", NULL, AT_NONE); //Cmdline_reparse_mainhall
@@ -527,6 +529,7 @@ cmdline_parm show_video_info("-show_video_info", NULL, AT_NONE); //Cmdline_show_
cmdline_parm frame_profile_arg("-profile_frame_time", NULL, AT_NONE); //Cmdline_frame_profile
cmdline_parm debug_window_arg("-debug_window", NULL, AT_NONE); // Cmdline_debug_window
cmdline_parm graphics_debug_output_arg("-gr_debug", nullptr, AT_NONE); // Cmdline_graphics_debug_output
cmdline_parm gr_sync_validation_arg("-gr_sync_validation", nullptr, AT_NONE); // Cmdline_gr_sync_validation (implies -gr_debug)
cmdline_parm log_to_stdout_arg("-stdout_log", nullptr, AT_NONE); // Cmdline_log_to_stdout
cmdline_parm slow_frames_ok_arg("-slow_frames_ok", nullptr, AT_NONE); // Cmdline_slow_frames_ok
cmdline_parm fixed_seed_rand("-seed", nullptr, AT_INT); // Cmdline_rng_seed,Cmdline_reuse_rng_seed;
@@ -534,6 +537,7 @@ cmdline_parm luadev_arg("-luadev", "Make lua errors non-fatal", AT_NONE); // Cmd
cmdline_parm override_arg("-override_data", "Enable override directory", AT_NONE); // Cmdline_override_data
cmdline_parm imgui_debug_arg("-imgui_debug", nullptr, AT_NONE);
cmdline_parm vulkan("-vulkan", nullptr, AT_NONE);
cmdline_parm opengl("-opengl", nullptr, AT_NONE);
cmdline_parm multithreading("-threads", nullptr, AT_INT);
char *Cmdline_start_mission = NULL;
@@ -567,12 +571,13 @@ bool Cmdline_frame_profile = false;
bool Cmdline_show_video_info = false;
bool Cmdline_debug_window = false;
bool Cmdline_graphics_debug_output = false;
bool Cmdline_gr_sync_validation = false;
bool Cmdline_log_to_stdout = false;
bool Cmdline_slow_frames_ok = false;
bool Cmdline_lua_devmode = false;
bool Cmdline_override_data = false;
bool Cmdline_show_imgui_debug = false;
bool Cmdline_vulkan = false;
GraphicsAPI Cmdline_graphics_api = GraphicsAPI::Default;
int Cmdline_multithreading = 1;
// Other
@@ -1822,6 +1827,22 @@ bool SetCmdlineParams()
if(center_res_arg.found()){
Cmdline_center_res = center_res_arg.str();
}
if (hdr_nits_arg.found()) {
// presence of the flag alone forces HDR output on, overriding the in-game HDR setting
options::OptionsManager::instance()->setOverride("Graphics.HDR", "true");
if (hdr_nits_arg.has_param()) {
float paperwhite_nits = 0.0f, peak_nits = 0.0f;
if (sscanf(hdr_nits_arg.str(), "%f,%f", &paperwhite_nits, &peak_nits) == 2 && paperwhite_nits > 0.0f && peak_nits > 0.0f) {
// std::to_string always includes a decimal point, so this is a valid JSON real
// (json_unpack's "f" format rejects a bare integer like "400")
options::OptionsManager::instance()->setOverride("Graphics.HDRPaperWhite", std::to_string(paperwhite_nits));
options::OptionsManager::instance()->setOverride("Graphics.HDRPeakLuminance", std::to_string(peak_nits));
} else {
Warning(LOCATION, "Failed to parse -hdr parameter \"%s\". Must be in format \"<paperwhite_nits>,<peak_nits>\".\n", hdr_nits_arg.str());
}
}
}
if(almission_arg.found()){//DTP for autoload mission // developer oritentated
Cmdline_almission = almission_arg.str();
Cmdline_use_last_pilot = 1;
@@ -2244,6 +2265,14 @@ bool SetCmdlineParams()
}
}
if( rt_shadows_arg.found() )
{
// Equivalent to picking "Raytraced" in the Shadow Method option. Silently
// has no effect if the renderer/hardware doesn't support
// CAPABILITY_RAYTRACED_SHADOWS -- the CSM path is used either way.
Shadow_render_method = ShadowRenderMethod::Raytraced;
}
if( no_deferred_lighting_arg.found() )
{
Cmdline_no_deferred_lighting = 1;
@@ -2307,6 +2336,16 @@ bool SetCmdlineParams()
Cmdline_graphics_debug_output = true;
}
if (gr_sync_validation_arg.found()) {
// Enables the Vulkan validation layer + debug messenger + synchronization
// validation feature (see VulkanRenderer::initializeInstance), but NOT the
// engine-level graphics debug output: -gr_debug additionally draws debug
// overlays (e.g. output_uniform_debug_data), which are unrelated to GPU
// sync validation and would otherwise appear as spurious on-screen
// artifacts. Keep this a pure GPU-validation switch.
Cmdline_gr_sync_validation = true;
}
if (log_to_stdout_arg.found()) {
Cmdline_log_to_stdout = true;
}
@@ -2332,7 +2371,10 @@ bool SetCmdlineParams()
}
if (vulkan.found()) {
Cmdline_vulkan = true;
Cmdline_graphics_api = GraphicsAPI::Vulkan;
}
else if (opengl.found()) {
Cmdline_graphics_api = GraphicsAPI::OpenGL;
}
//Deprecated flags - CommanderDJ
+3 -1
View File
@@ -13,6 +13,7 @@
#define FS_CMDLINE_HEADER_FILE
#include <optional>
#include "graphics/2d.h"
int parse_cmdline(int argc, char *argv[]);
@@ -153,12 +154,13 @@ extern bool Cmdline_frame_profile;
extern bool Cmdline_show_video_info;
extern bool Cmdline_debug_window;
extern bool Cmdline_graphics_debug_output;
extern bool Cmdline_gr_sync_validation;
extern bool Cmdline_log_to_stdout;
extern bool Cmdline_slow_frames_ok;
extern bool Cmdline_lua_devmode;
extern bool Cmdline_override_data;
extern bool Cmdline_show_imgui_debug;
extern bool Cmdline_vulkan;
extern GraphicsAPI Cmdline_graphics_api;
extern int Cmdline_multithreading;
enum class WeaponSpewType { NONE = 0, STANDARD, ALL };
+3 -1
View File
@@ -4,6 +4,8 @@
#include "cutscene/VideoPresenter.h"
#include "cutscene/ffmpeg/FFMPEGDecoder.h"
#include "cmdline/cmdline.h"
#include "graphics/2d.h"
#include "globalincs/alphacolors.h"
@@ -52,7 +54,7 @@ void videoPlaybackInit(PlayerState* state) {
Assert(state != NULL);
if (gr_screen.mode != GR_STUB) {
if (gr_screen.mode != GraphicsAPI::Stub) {
// The video presenter is independent of the underlying graphics API
state->videoPresenter.reset(new VideoPresenter(state->props));
}
+4 -10
View File
@@ -1,18 +1,12 @@
#include "ddsutils/ddsutils.h"
#include "cfile/cfile.h"
#include "graphics/2d.h"
#include "osapi/osregistry.h"
#ifdef USE_OPENGL_ES
#include "graphics/opengl/es_compatibility.h"
#endif
#ifdef WITH_OPENGL
#include <glad/glad.h>
#else
static constexpr int GLAD_GL_EXT_texture_compression_s3tc = 0;
static constexpr int GLAD_GL_ARB_texture_compression_bptc = 0;
#endif
#define BCDEC_IMPLEMENTATION 1
PUSH_SUPPRESS_WARNINGS
#include "ddsutils/bcdec.h"
@@ -60,11 +54,11 @@ static bool conversion_needed(const DDS_HEADER &dds_header)
case FOURCC_DXT1:
case FOURCC_DXT3:
case FOURCC_DXT5:
return !GLAD_GL_EXT_texture_compression_s3tc;
return !gr_is_capable(gr_capability::CAPABILITY_S3TC);
case FOURCC_DX10:
// anything other than BC7 will end up invalid
return !GLAD_GL_ARB_texture_compression_bptc;
return !gr_is_capable(gr_capability::CAPABILITY_BPTC);
default:
break;
@@ -226,7 +220,7 @@ static size_t compute_dds_size(const DDS_HEADER &dds_header, bool converting = f
if (dds_header.ddspf.dwFlags & DDPF_FOURCC) {
// size of data block (4x4)
d_size += ((d_width + 3) / 4) * ((d_height + 3) / 4) * d_depth * ((dds_header.ddspf.dwFourCC == FOURCC_DXT1) ? 8 : 16);
d_size += dds_compressed_mip_size(d_width, d_height, (dds_header.ddspf.dwFourCC == FOURCC_DXT1) ? 8 : 16) * d_depth;
} else {
d_size += d_width * d_height * d_depth * (dds_header.ddspf.dwRGBBitCount / 8);
}
+21
View File
@@ -273,6 +273,27 @@ typedef struct {
} DDS_HEADER_DXT10;
#pragma pack()
// Block size in bytes for a 4x4 texel block of a compressed DDS format.
// comp_type is one of the DDS_DXT*/DDS_CUBEMAP_DXT* constants.
inline int dds_block_size(int comp_type) {
switch (comp_type) {
case DDS_DXT1:
case DDS_CUBEMAP_DXT1:
return 8;
case DDS_DXT3: case DDS_CUBEMAP_DXT3:
case DDS_DXT5: case DDS_CUBEMAP_DXT5:
case DDS_BC7:
return 16;
default:
return 0;
}
}
// Size in bytes of one mip level of a block-compressed texture.
inline size_t dds_compressed_mip_size(int w, int h, int block_size) {
return static_cast<size_t>(((w + 3) / 4) * ((h + 3) / 4) * block_size);
}
#define DDS_OFFSET 4+sizeof(DDS_HEADER) //place where the data starts -- should be 128
#define DX10_OFFSET DDS_OFFSET+sizeof(DDS_HEADER_DXT10) // Unless a DX10 header is present
+1 -1
View File
@@ -267,7 +267,7 @@ float smoothstep(float edge0, float edge1, float x) {
namespace decals {
void initialize() {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
Decal_system_active = false;
return;
}
+14
View File
@@ -1,6 +1,19 @@
#include "gamma.sdr"
#ifdef VULKAN
layout (location = 0) in vec4 fragTexCoord;
layout (location = 1) in vec4 fragColor;
layout (location = 0) out vec4 fragOut0;
layout (set = 1, binding = 1) uniform sampler2DArray baseMap;
layout (set = 2, binding = 0, std140) uniform genericData {
vec4 color;
float intensity;
};
#else
in vec4 fragTexCoord;
in vec4 fragColor;
@@ -12,6 +25,7 @@ layout (std140) uniform genericData {
vec4 color;
float intensity;
};
#endif
void main()
{
+19
View File
@@ -1,3 +1,21 @@
#ifdef VULKAN
layout (location = 0) in vec4 vertPosition;
layout (location = 1) in vec4 vertColor;
layout (location = 2) in vec4 vertTexCoord;
layout (location = 0) out vec4 fragTexCoord;
layout (location = 1) out vec4 fragColor;
layout (set = 2, binding = 1, std140) uniform matrixData {
mat4 modelViewMatrix;
mat4 projMatrix;
};
layout (set = 2, binding = 0, std140) uniform genericData {
vec4 color;
float intensity;
};
#else
in vec4 vertPosition;
in vec4 vertTexCoord;
in vec4 vertColor;
@@ -14,6 +32,7 @@ layout (std140) uniform genericData {
vec4 color;
float intensity;
};
#endif
void main()
{
+12 -3
View File
@@ -1,11 +1,20 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D bloomed;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D bloomed;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(std140, set = 2, binding = 0)
#else
layout(std140)
#endif
uniform genericData {
float bloom_intensity;
int levels;
};
+15 -5
View File
@@ -1,16 +1,26 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
out vec4 fragOut0;
in vec4 fragTexCoord;
uniform sampler2D tex;
#endif
const float TapSize = 1.0;
layout (std140) uniform genericData {
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
float texSize;
int level;
int pad[2];
};
const float TapSize = 1.0;
// Gaussian Blur
// 2 passes required
void main()
@@ -1,6 +1,12 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
const float Luminance = 0.08;
const float fMiddleGray = 0.2;
const float fWhiteCutoff = 0.4;
+14 -2
View File
@@ -1,17 +1,29 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
#ifdef COPY_ARRAY
uniform sampler2DArray tex;
#else
uniform sampler2D tex;
#endif
#endif
void main()
{
#ifdef COPY_ARRAY
#if defined(COPY_ARRAY) && !defined(VULKAN)
fragOut0 = texture(tex, vec3(fragTexCoord.xy, 0));
#else
fragOut0 = texture(tex, fragTexCoord.xy);
#endif
#ifdef CLAMP01
// Used to derive a [0,1]-bounded proxy from an extended-range HDR source,
// e.g. for FXAA/SMAA edge detection when the real output carries values
// above 1.0.
fragOut0 = min(fragOut0, vec4(1.0));
#endif
}
+62 -9
View File
@@ -6,6 +6,29 @@
#include "normals.sdr"
#include "gamma.sdr"
#ifdef VULKAN
// G-buffer: 6 attachments, 1/3/5 are write-masked by pipeline blend state
layout (location = 0) out vec4 fragOut0; // Color/Diffuse
layout (location = 1) out vec4 fragOut1; // Position (masked)
layout (location = 2) out vec4 fragOut2; // Normal
layout (location = 3) out vec4 fragOut3; // Specular (masked)
layout (location = 4) out vec4 fragOut4; // Emissive
layout (location = 5) out vec4 fragOut5; // Composite (masked)
layout (location = 0) flat in mat4 invModelMatrix; // locations 0-3
layout (location = 4) flat in vec3 decalDirection;
layout (location = 5) flat in float normal_angle_cutoff;
layout (location = 6) flat in float angle_fade_start;
layout (location = 7) flat in float alpha_scale;
layout (set = 1, binding = 1) uniform sampler2DArray decalTextures;
#define diffuseMap decalTextures
#define glowMap decalTextures
#define normalMap decalTextures
layout (set = 1, binding = 4) uniform sampler2D gDepthBuffer;
layout (set = 1, binding = 6) uniform sampler2D gNormalBuffer;
#else
out vec4 fragOut0; // Diffuse buffer
out vec4 fragOut1; // Normal buffer
out vec4 fragOut2; // Emissive buffer
@@ -22,8 +45,14 @@ uniform sampler2D gNormalBuffer;
uniform sampler2DArray diffuseMap;
uniform sampler2DArray glowMap;
uniform sampler2DArray normalMap;
#endif
layout (std140) uniform decalGlobalData {
#ifdef VULKAN
layout (set = 1, binding = 2, std140)
#else
layout (std140)
#endif
uniform decalGlobalData {
mat4 viewMatrix;
mat4 projMatrix;
mat4 invViewMatrix;
@@ -32,7 +61,12 @@ layout (std140) uniform decalGlobalData {
vec2 viewportSize;
};
layout (std140) uniform decalInfoData {
#ifdef VULKAN
layout (set = 2, binding = 3, std140)
#else
layout (std140)
#endif
uniform decalInfoData {
int diffuse_index;
int glow_index;
int normal_index;
@@ -45,8 +79,16 @@ vec3 computeViewPosition(vec2 textureCoord) {
vec4 clipSpaceLocation;
vec2 normalizedCoord = textureCoord / viewportSize;
#ifdef VULKAN
clipSpaceLocation.x = normalizedCoord.x * 2.0 - 1.0;
// Vulkan negative viewport inverts Y mapping
clipSpaceLocation.y = 1.0 - normalizedCoord.y * 2.0;
// Vulkan depth is [0,1] — use directly
clipSpaceLocation.z = texelFetch(gDepthBuffer, ivec2(textureCoord), 0).r;
#else
clipSpaceLocation.xy = normalizedCoord * 2.0f - 1.0f;
clipSpaceLocation.z = texelFetch(gDepthBuffer, ivec2(textureCoord), 0).r * 2.0f - 1.0f;
#endif
clipSpaceLocation.w = 1.0f;
vec4 homogenousLocation = invProjMatrix * clipSpaceLocation;
@@ -57,24 +99,24 @@ vec3 computeViewPosition(vec2 textureCoord) {
vec3 getPixelNormal(vec3 frag_position, vec2 tex_coord, inout float alpha, out vec3 binormal, out vec3 tangent) {
#ifdef USE_NORMAL_MAP
// If we can then we just use the existing normal buffer
vec3 normal = texelFetch(gNormalBuffer, ivec2(tex_coord), 0).xyz;
vec3 normal = texelFetch(gNormalBuffer, ivec2(tex_coord), 0).xyz;
// If we use the normal map then we don't really need these values so we don't need to compute them here
binormal = vec3(0.0);
tangent = vec3(0.0);
binormal = vec3(0.0);
tangent = vec3(0.0);
#else
// Use some fancy screen-space derivates to determine the normal of the current pixel by looking at the surrounding pixels
vec3 pos_dx = dFdx(frag_position);
vec3 pos_dy = dFdy(frag_position);
vec3 normal = normalize(cross(pos_dx, pos_dy));
vec3 normal = normalize(cross(pos_dx, pos_dy));
binormal = normalize(pos_dx);
tangent = normalize(pos_dy);
binormal = normalize(pos_dx);
tangent = normalize(pos_dy);
#endif
//Calculate angle between surface normal and decal direction
float angle = acos(dot(normal, decalDirection));
float angle = acos(clamp(dot(normal, decalDirection), -1.0, 1.0));
if (angle > normal_angle_cutoff) {
// The angle between surface normal and decal direction is too big
@@ -162,7 +204,18 @@ void main() {
normal_out = tangentToView * decalNormal * alpha;
}
#ifdef VULKAN
// 6-attachment G-buffer; attachments 1/3/5 are write-masked
fragOut0 = diffuse_out;
fragOut1 = vec4(0.0);
fragOut2 = vec4(normal_out, 0.0);
fragOut3 = vec4(0.0);
fragOut4 = emissive_out;
fragOut5 = vec4(0.0);
#else
// OpenGL: 3 draw buffers mapped to attachments 0/2/4
fragOut0 = diffuse_out;
fragOut1 = vec4(normal_out, 0.0);
fragOut2 = emissive_out;
#endif
}
+34 -2
View File
@@ -1,4 +1,20 @@
#ifdef VULKAN
// Binding 0: box vertex positions
layout (location = 0) in vec4 vertPosition;
// Binding 1: per-instance model matrix (mat4 = 4 vec4s at locations 8-11)
layout (location = 8) in vec4 vertModelMatrix0;
layout (location = 9) in vec4 vertModelMatrix1;
layout (location = 10) in vec4 vertModelMatrix2;
layout (location = 11) in vec4 vertModelMatrix3;
layout (location = 0) flat out mat4 invModelMatrix; // locations 0-3
layout (location = 4) flat out vec3 decalDirection;
layout (location = 5) flat out float normal_angle_cutoff;
layout (location = 6) flat out float angle_fade_start;
layout (location = 7) flat out float alpha_scale;
#else
in vec4 vertPosition;
in mat4 vertModelMatrix;
@@ -7,8 +23,14 @@ flat out vec3 decalDirection;
flat out float normal_angle_cutoff;
flat out float angle_fade_start;
flat out float alpha_scale;
#endif
layout (std140) uniform decalGlobalData {
#ifdef VULKAN
layout (set = 1, binding = 2, std140)
#else
layout (std140)
#endif
uniform decalGlobalData {
mat4 viewMatrix;
mat4 projMatrix;
mat4 invViewMatrix;
@@ -17,7 +39,12 @@ layout (std140) uniform decalGlobalData {
vec2 viewportSize;
};
layout (std140) uniform decalInfoData {
#ifdef VULKAN
layout (set = 2, binding = 3, std140)
#else
layout (std140)
#endif
uniform decalInfoData {
int diffuse_index;
int glow_index;
int normal_index;
@@ -27,6 +54,11 @@ layout (std140) uniform decalInfoData {
};
void main() {
#ifdef VULKAN
// Reconstruct per-instance model matrix from 4 vec4 columns
mat4 vertModelMatrix = mat4(vertModelMatrix0, vertModelMatrix1, vertModelMatrix2, vertModelMatrix3);
#endif
normal_angle_cutoff = vertModelMatrix[0][3];
angle_fade_start = vertModelMatrix[1][3];
alpha_scale = vertModelMatrix[2][3];
@@ -1,14 +1,9 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
#include "gamma.sdr"
layout (location = 0) in vec4 fragTexCoord;
layout (location = 1) in vec4 fragColor;
layout (location = 0) out vec4 fragOut0;
layout (binding = 1, std140) uniform genericData {
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
mat4 modelMatrix;
vec4 color;
@@ -22,10 +17,29 @@ layout (binding = 1, std140) uniform genericData {
float intensity;
float alphaThreshold;
bool clipEnabled;
uint clipEnabled;
};
layout(binding = 2) uniform sampler2DArray baseMap;
#ifdef VULKAN
layout(set = 1, binding = 1) uniform sampler2DArray baseMap;
layout(location = 0) in vec4 fragTexCoord;
layout(location = 1) in vec4 fragColor;
layout(location = 0) out vec4 fragOut0;
#else
uniform sampler2DArray baseMap;
in vec4 fragTexCoord;
in vec4 fragColor;
out vec4 fragOut0;
#endif
#include "gamma.sdr"
void main()
{
@@ -0,0 +1,70 @@
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
mat4 modelMatrix;
vec4 color;
vec4 clipEquation;
int baseMapIndex;
int alphaTexture;
int noTexturing;
int srgb;
float intensity;
float alphaThreshold;
uint clipEnabled;
};
#ifdef VULKAN
layout(set = 2, binding = 1, std140)
#else
layout(std140)
#endif
uniform matrixData {
mat4 modelViewMatrix;
mat4 projMatrix;
};
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
layout(location = 1) in vec4 vertColor;
layout(location = 2) in vec4 vertTexCoord;
layout(location = 0) out vec4 fragTexCoord;
layout(location = 1) out vec4 fragColor;
#else
out float gl_ClipDistance[1];
out vec4 fragTexCoord;
in vec4 vertTexCoord;
out vec4 fragColor;
in vec4 vertColor;
in vec4 vertPosition;
#endif
void main()
{
fragTexCoord = vertTexCoord;
fragColor = vertColor * color;
gl_Position = projMatrix * modelViewMatrix * vertPosition;
if (clipEnabled != 0u) {
gl_ClipDistance[0] = dot(clipEquation, modelMatrix * vertPosition);
} else {
#ifdef VULKAN
// Vulkan has no glEnable(GL_CLIP_DISTANCE0) equivalent — clip distances are
// always evaluated when declared in the shader. Must write a positive value
// when clipping is disabled to prevent undefined-value vertex culling.
gl_ClipDistance[0] = 1.0;
#endif
}
}
+71 -12
View File
@@ -4,8 +4,30 @@
#include "shadows.sdr" //! #include "shadows.sdr"
#include "z-compress.sdr" //! #include "z-compress.sdr"
#ifdef VULKAN
layout(location = 0) out vec4 fragOut0;
#else
out vec4 fragOut0;
#endif
#ifdef VULKAN
layout(set = 1, binding = 1) uniform sampler2D sTextures[16];
#define ColorBuffer sTextures[0]
#define NormalBuffer sTextures[1]
#define PositionBuffer sTextures[2]
#define SpecBuffer sTextures[3]
layout(set = 0, binding = 2) uniform sampler2DArrayShadow shadow_map;
#ifdef ENV_MAP
layout(set = 0, binding = 3) uniform samplerCube sEnvmap;
layout(set = 0, binding = 4) uniform samplerCube sIrrmap;
#endif
#ifdef RT_SHADOWS
layout(set = 0, binding = 5) uniform accelerationStructureEXT shadow_tlas;
#endif
#else
uniform sampler2D ColorBuffer;
uniform sampler2D NormalBuffer;
uniform sampler2D PositionBuffer;
@@ -16,8 +38,14 @@ uniform sampler2DArrayShadow shadow_map;
uniform samplerCube sEnvmap;
uniform samplerCube sIrrmap;
#endif
#endif
layout (std140) uniform globalDeferredData {
#ifdef VULKAN
layout(set = 0, binding = 1, std140)
#else
layout(std140)
#endif
uniform globalDeferredData {
mat4 inv_view_matrix;
float invScreenWidth;
@@ -25,9 +53,16 @@ layout (std140) uniform globalDeferredData {
float nearPlane;
};
layout (std140) uniform shadowCascadeParams {
#ifdef VULKAN
layout(set = 0, binding = 6, std140)
#else
layout(std140)
#endif
uniform shadowCascadeParams {
int cascade_offset;
int cascade_count;
float rtShadowBiasMin;
float rtShadowBiasMax;
mat4 shadow_mv_matrix;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
@@ -35,12 +70,22 @@ layout (std140) uniform shadowCascadeParams {
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
};
layout (std140) uniform matrixData {
#ifdef VULKAN
layout(set = 2, binding = 1, std140)
#else
layout(std140)
#endif
uniform matrixData {
mat4 modelViewMatrix;
mat4 projMatrix;
};
layout (std140) uniform lightData {
#ifdef VULKAN
layout(set = 0, binding = 0, std140)
#else
layout(std140)
#endif
uniform lightData {
vec3 diffuseLightColor;
float coneAngle;
@@ -48,18 +93,18 @@ layout (std140) uniform lightData {
float coneInnerAngle;
vec3 coneDir;
bool dualCone;
int dualCone;
vec3 scale;
float lightRadius;
int lightType;
bool enable_shadows;
int enable_shadows;
float sourceRadius;
};
// Nearest point sphere and tube light calculations taken from
// Nearest point sphere and tube light calculations taken from
// "Real Shading in Unreal Engine 4" by Brian Karis, Epic Games
// Part of SIGGRAPH 2013 Course: Physically Based Shading in Theory and Practice
@@ -67,7 +112,7 @@ vec3 ExpandLightSize(in vec3 lightDir, in vec3 reflectDir) {
// There's an extra max(...,sourceRadius) call here vs the version in the paper.
// This prevents the centerToRay calculation from choosing a point behind
// the reflection ray's origin (i.e. underneath the surface).
// this is necessary for situations where the fragment being shaded lies inside
// this is necessary for situations where the fragment being shaded lies inside
// the sourceRadius of the light.
// Instead, we choose a point suffciently far away from the reflection origin (hence max(...,sourceRadius))
// so that we have a gradual transition as the shaded fragments along the surface
@@ -75,12 +120,13 @@ vec3 ExpandLightSize(in vec3 lightDir, in vec3 reflectDir) {
vec3 centerToRay = max(dot(lightDir, reflectDir),sourceRadius) * reflectDir - lightDir;
return lightDir + centerToRay * clamp(sourceRadius/length(centerToRay), 0.0, 1.0);
}
void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 lightDirOut, out float attenuation, out float area_normalisation)
void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 lightDirOut, out float attenuation, out float area_normalisation, out float lightDist)
{
if (lightType == LT_DIRECTIONAL) {
lightDirOut = normalize(lightDir);
attenuation = 1.0;
area_normalisation = 1.0;
lightDist = RT_SHADOW_MAX_DISTANCE;
} else {
vec3 lightPosition = modelViewMatrix[3].xyz;
// Positional light source
@@ -93,6 +139,7 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
// move lightDirOut towards the ray defined by reflectDir
lightDirOut = ExpandLightSize(lightDirOut, reflectDir);
dist = length(lightDirOut);
lightDist = dist;
// Energy conservation term
float alpha_adjust = clamp(alpha + (sourceRadius/(2*dist)), 0.0, 1.0);
area_normalisation = alpha/alpha_adjust;
@@ -135,6 +182,7 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
// Same principle as in LT_POINT, treat chosen location as a spherelight.
lightDirOut = ExpandLightSize(lightDirOut, reflectDir);
float dist = length(lightDirOut);
lightDist = dist;
// Energy conservation term
float alpha_adjust = min(alpha + (sourceRadius/(2*dist)), 1.0);
area_normalisation = alpha/alpha_adjust;
@@ -150,10 +198,11 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
lightDirOut = lightPosition - position.xyz;
float coneDot = dot(normalize(-lightDirOut), coneDir);
float dist = length(lightDirOut);
lightDist = dist;
attenuation = 1.0 - clamp(sqrt(dist / lightRadius), 0.0, 1.0);
area_normalisation = 1.0;
if(dualCone) {
if(dualCone != 0) {
if(abs(coneDot) < coneAngle) {
discard;
} else {
@@ -256,9 +305,18 @@ void main()
vec3 lightDir;
float attenuation;
float area_normalisation;
GetLightInfo(position, alpha, reflectDir, lightDir, attenuation, area_normalisation);
float lightDist;
GetLightInfo(position, alpha, reflectDir, lightDir, attenuation, area_normalisation, lightDist);
if (enable_shadows) {
if (enable_shadows != 0) {
#ifdef RT_SHADOWS
vec3 worldPos = (inv_view_matrix * vec4(position, 1.0)).xyz;
vec3 worldNormal = normalize((inv_view_matrix * vec4(normal, 0.0)).xyz);
vec3 worldLightDir = normalize((inv_view_matrix * vec4(lightDir, 0.0)).xyz);
float rtShadowBias = computeRtShadowBias(length(position), rtShadowBiasMin, rtShadowBiasMax);
attenuation *= traceShadowRay(shadow_tlas, worldPos, worldNormal, worldLightDir, lightDist, rtShadowBias);
#else
vec4 fragShadowPos = shadow_mv_matrix * inv_view_matrix * vec4(position, 1.0);
vec4 fragShadowUV[NUM_SHADOW_CASCADES];
for (int i = 0; i < NUM_SHADOW_CASCADES; i++) {
@@ -266,6 +324,7 @@ void main()
}
attenuation *= getShadowValue(shadow_map, -position.z, fragShadowUV, cascade_distances, smoothness_factors, cascade_offset, cascade_count);
#endif
}
vec3 halfVec = normalize(lightDir + eyeDir);
+24 -4
View File
@@ -1,14 +1,28 @@
#include "lighting.sdr"
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
#else
in vec4 vertPosition;
#endif
layout (std140) uniform matrixData {
#ifdef VULKAN
layout(set = 2, binding = 1, std140)
#else
layout(std140)
#endif
uniform matrixData {
mat4 modelViewMatrix;
mat4 projMatrix;
};
layout (std140) uniform lightData {
#ifdef VULKAN
layout(set = 0, binding = 0, std140)
#else
layout(std140)
#endif
uniform lightData {
vec3 diffuseLightColor;
float coneAngle;
@@ -16,13 +30,13 @@ layout (std140) uniform lightData {
float coneInnerAngle;
vec3 coneDir;
bool dualCone;
int dualCone;
vec3 scale;
float lightRadius;
int lightType;
bool enable_shadows;
int enable_shadows;
float sourceRadius;
};
@@ -30,7 +44,13 @@ layout (std140) uniform lightData {
void main()
{
if (lightType == LT_DIRECTIONAL || lightType == LT_AMBIENT) {
#ifdef VULKAN
// Fullscreen triangle from gl_VertexIndex (no vertex buffer)
vec2 pos = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
#else
gl_Position = vec4(vertPosition.xyz, 1.0);
#endif
} else {
gl_Position = projMatrix * modelViewMatrix * vec4(vertPosition.xyz * scale, 1.0);
}
@@ -1,3 +1,15 @@
#ifdef VULKAN
layout(location = 0) in vec4 fragTexCoord;
layout(location = 1) in vec4 fragColor;
layout(location = 2) in float fragOffset;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray baseMap;
layout(set = 1, binding = 4) uniform sampler2D depthMap;
layout(set = 1, binding = 5) uniform sampler2D frameBuffer;
layout(set = 1, binding = 6) uniform sampler2D distMap;
#else
in vec4 fragTexCoord;
in vec4 fragColor;
in float fragOffset;
@@ -8,8 +20,14 @@ uniform sampler2DArray baseMap;
uniform sampler2D depthMap;
uniform sampler2D distMap;
uniform sampler2D frameBuffer;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(std140, set = 2, binding = 0)
#else
layout (std140)
#endif
uniform genericData {
float window_width;
float window_height;
float use_offset;
@@ -1,3 +1,13 @@
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
layout(location = 1) in vec4 vertColor;
layout(location = 2) in vec4 vertTexCoord;
layout(location = 6) in float vertRadius;
layout(location = 0) out vec4 fragTexCoord;
layout(location = 1) out vec4 fragColor;
layout(location = 2) out float fragOffset;
#else
in vec4 vertPosition;
in vec4 vertTexCoord;
in vec4 vertColor;
@@ -5,13 +15,24 @@ in float vertRadius;
out vec4 fragTexCoord;
out vec4 fragColor;
out float fragOffset;
#endif
layout (std140) uniform matrixData {
#ifdef VULKAN
layout(std140, set = 2, binding = 1)
#else
layout (std140)
#endif
uniform matrixData {
mat4 modelViewMatrix;
mat4 projMatrix;
};
layout (std140) uniform genericData {
#ifdef VULKAN
layout(std140, set = 2, binding = 0)
#else
layout (std140)
#endif
uniform genericData {
float window_width;
float window_height;
float use_offset;
+25 -2
View File
@@ -2,6 +2,17 @@
#include "gamma.sdr"
#include "z-compress.sdr"
#ifdef VULKAN
layout(location = 0) in vec4 fragTexCoord;
layout(location = 1) in vec4 fragColor;
layout(location = 2) in float fragRadius;
layout(location = 3) in vec4 fragPosition;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray baseMap;
layout(set = 1, binding = 4) uniform sampler2D depthMap;
#else
in float fragRadius;
in vec4 fragPosition;
in vec4 fragTexCoord;
@@ -11,8 +22,14 @@ out vec4 fragOut0;
uniform sampler2DArray baseMap;
uniform sampler2D depthMap;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(std140, set = 2, binding = 0)
#else
layout(std140)
#endif
uniform genericData {
float window_width;
float window_height;
float nearZ;
@@ -39,7 +56,13 @@ void main()
float sceneDepthLinear;
float fragDepthLinear;
if ( linear_depth == 1 ) {
sceneDepthLinear = -uncompress_depth_value(sceneDepth.z);
// Position buffer .z == 0 means no deferred geometry was rendered at this pixel
// (G-buffer was cleared to zero). Treat as max distance so particles render fully.
if (sceneDepth.z == 0.0) {
sceneDepthLinear = farZ;
} else {
sceneDepthLinear = -uncompress_depth_value(sceneDepth.z);
}
fragDepthLinear = -fragPosition.z;
} else {
sceneDepthLinear = ( 2.0 * farZ * nearZ ) / ( farZ + nearZ - sceneDepth.x * (farZ-nearZ) );
+19 -2
View File
@@ -1,4 +1,15 @@
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
layout(location = 1) in vec4 vertColor;
layout(location = 2) in vec4 vertTexCoord;
layout(location = 6) in float vertRadius;
layout(location = 0) out vec4 fragTexCoord;
layout(location = 1) out vec4 fragColor;
layout(location = 2) out float fragRadius;
layout(location = 3) out vec4 fragPosition;
#else
in vec4 vertPosition;
in vec4 vertTexCoord;
in vec4 vertColor;
@@ -16,15 +27,21 @@ in float vertRadius;
out vec4 fragTexCoord;
out vec4 fragColor;
#endif
#endif
layout (std140) uniform matrixData {
#ifdef VULKAN
layout(std140, set = 2, binding = 1)
#else
layout(std140)
#endif
uniform matrixData {
mat4 modelViewMatrix;
mat4 projMatrix;
};
void main()
{
#ifdef FLAG_EFFECT_GEOMETRY
#if !defined(VULKAN) && defined(FLAG_EFFECT_GEOMETRY)
geoRadius = vertRadius;
geoUvec = vertUvec;
gl_Position = modelViewMatrix * vertPosition;
+14 -1
View File
@@ -1,14 +1,27 @@
#include "gamma.sdr"
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
layout(set = 1, binding = 4) uniform sampler2D depth_tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
uniform sampler2D depth_tex;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(std140, set = 2, binding = 0)
#else
layout (std140)
#endif
uniform genericData {
vec3 fog_color;
float fog_start;
+17
View File
@@ -487,10 +487,27 @@ FxaaFloat4 FxaaPixelShader(
return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM);
#endif
}
#ifdef VULKAN
layout(set = 1, binding = 1) uniform sampler2D tex0;
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(std140, set = 2, binding = 0) uniform genericData {
float rt_w;
float rt_h;
float pad0;
float pad1;
};
#else
uniform sampler2D tex0;
in vec2 v_rcpFrame;
noperspective in vec2 v_pos;
out vec4 fragOut0;
#endif
void main() {
#ifdef VULKAN
vec2 v_rcpFrame = vec2(1.0 / rt_w, 1.0 / rt_h);
vec2 v_pos = fragTexCoord;
#endif
fragOut0 = FxaaPixelShader(v_pos, tex0, v_rcpFrame, FXAA_QUALITY_SUBPIX, FXAA_QUALITY_EDGE_THRESHOLD, FXAA_QUALITY_EDGE_THRESHOLD_MIN);
}
+9
View File
@@ -1,4 +1,12 @@
#ifdef VULKAN
layout(location = 0) out vec2 fragTexCoord;
void main() {
vec2 uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
fragTexCoord = uv;
gl_Position = vec4(uv * 2.0 - 1.0, 0.0, 1.0);
}
#else
in vec4 vertPosition;
out vec2 v_rcpFrame;
@@ -14,3 +22,4 @@ void main() {
v_rcpFrame = vec2(1.0/rt_w, 1.0/rt_h);
v_pos = vertPosition.xy*0.5 + 0.5;
}
#endif
+16 -1
View File
@@ -1,7 +1,22 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
// [0]=real color (passed through as-is), [1]=luma detection proxy (see
// getAADetectionView; may be the same view as [0] when not HDR)
layout(set = 1, binding = 1) uniform sampler2D fxaaTex[2];
#define tex fxaaTex[0]
#define lumaTex fxaaTex[1]
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
void main() {
vec4 color = texture(tex, fragTexCoord.xy);
#ifdef VULKAN
vec3 lumaColor = texture(lumaTex, fragTexCoord.xy).rgb;
fragOut0 = vec4(color.rgb, dot(lumaColor, vec3(0.299, 0.587, 0.114)));
#else
fragOut0 = vec4(color.rgb, dot(color.rgb, vec3(0.299, 0.587, 0.114)) );
}
#endif
}
@@ -1,18 +1,55 @@
#include "gamma.sdr"
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
float gamma;
float hdr_paperwhite_nits; // HDR10_OUTPUT only
float hdr_peak_nits; // HDR10_OUTPUT only
float pad[1];
};
// Final whole-frame output-encode pass: applies the user gamma/brightness
// slider to the fully composited, display-ready frame (3D scene + menus/HUD
// alike) right before it's written into the swap chain image. The gamma curve
// is shared between the two output legs so the slider behaves consistently:
//
// * SDR (default, both backends): the input is already sRGB-encoded, so just
// apply the gamma slider. See VulkanRenderer::encodeToSwapChain().
// * HDR10_OUTPUT (Vulkan only, when the swap chain negotiated HDR10 =
// BT.2020 + ST.2084): the input is the shared extended-sRGB encoding from
// tonemapping-f.sdr's HdrScene tonemapper (1.0 == paper white). After the
// gamma slider, decode to linear, scale to display nits, convert to BT.2020
// primaries and apply PQ (ST.2084).
void main()
{
vec4 color = texture(tex, fragTexCoord.xy);
float g = max(gamma, 0.001);
#ifdef HDR10_OUTPUT
color.rgb = pow(max(color.rgb, vec3(0.0)), vec3(1.0 / g));
vec3 lin = srgb_to_linear(max(color.rgb, vec3(0.0)));
vec3 nits = min(lin * hdr_paperwhite_nits, vec3(hdr_peak_nits));
vec3 rec2020 = bt709_to_bt2020(nits / 10000.0);
fragOut0 = vec4(linear_to_pq(rec2020), 1.0);
#else
color.rgb = pow(color.rgb, vec3(1.0 / g));
fragOut0 = color;
#endif
}
+31
View File
@@ -27,3 +27,34 @@ vec3 linear_to_srgb(vec3 val) {
vec4 linear_to_srgb(vec4 val) {
return pow(val, vec4(SRGB_GAMMA_INVERSE));
}
// ===== HDR10 helpers (PQ / ST.2084 + BT.2020) =====
//
// These are used by the final output-encode pass when HDR10 output is active.
// The composition buffer stores values in an extended-sRGB encoding where 1.0
// maps to "paper white"; the encode pass decodes to linear, scales to display
// nits, converts BT.709 primaries to BT.2020, then applies the PQ transfer.
// BT.709 -> BT.2020 primary conversion (linear light). GLSL is column-major,
// so each vec3 below is a COLUMN of the standard row-major conversion matrix.
const mat3 bt709_to_bt2020_mat = mat3(
vec3(0.627403914, 0.069097158, 0.016391439),
vec3(0.329283028, 0.919540405, 0.088013308),
vec3(0.043313057, 0.011362437, 0.895595253));
vec3 bt709_to_bt2020(vec3 c) {
return bt709_to_bt2020_mat * c;
}
// SMPTE ST.2084 (PQ) inverse-EOTF / OETF. Input is linear luminance normalized
// so that 1.0 == 10000 nits (the PQ system peak). Output is the PQ-encoded
// signal in [0,1] suitable for an HDR10 (BT.2020 + ST.2084) swap chain.
vec3 linear_to_pq(vec3 L) {
const float m1 = 0.1593017578125;
const float m2 = 78.84375;
const float c1 = 0.8359375;
const float c2 = 18.8515625;
const float c3 = 18.6875;
vec3 Lm1 = pow(max(L, vec3(0.0)), vec3(m1));
return pow((c1 + c2 * Lm1) / (1.0 + c3 * Lm1), vec3(m2));
}
+16 -6
View File
@@ -1,16 +1,26 @@
//!#version 150
#include "gamma.sdr" //! #include "gamma.sdr"
#define PI 3.1415926535897932384626433832795
uniform samplerCube envmap;
layout (std140) uniform genericData {
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform samplerCube envmap;
#else
uniform samplerCube envmap;
in vec4 fragTexCoord;
out vec4 fragOut0;
#endif
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
int face;
};
in vec4 fragTexCoord;
out vec4 fragOut0;
// Iteratively optimised points to give low discrepancy distribution on arbitrary hemisphere.
const vec3 points[128] = vec3[128](
vec3(-0.2268, 0.6185, 0.7523),
+15 -1
View File
@@ -1,12 +1,26 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D textures[16];
#define scene textures[0]
#define cockpit textures[1]
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D scene;
uniform sampler2D cockpit;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(std140, set = 2, binding = 0)
#else
layout (std140)
#endif
uniform genericData {
vec2 sun_pos;
float density;
float weight;
+116 -29
View File
@@ -25,7 +25,12 @@ struct model_light {
};
layout (std140) uniform modelData {
#ifdef VULKAN
layout(set = 1, binding = 0, std140)
#else
layout(std140)
#endif
uniform modelData {
mat4 modelViewMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
@@ -44,7 +49,7 @@ layout (std140) uniform modelData {
vec4 clip_equation;
float thruster_scale;
bool use_clip_plane;
int use_clip_plane;
int n_lights;
float defaultGloss;
@@ -56,10 +61,10 @@ layout (std140) uniform modelData {
vec3 emissionFactor;
bool alphaGloss;
int alphaGloss;
bool gammaSpec;
bool envGloss;
int gammaSpec;
int envGloss;
int effect_num;
int sBasemapIndex;
@@ -72,7 +77,7 @@ layout (std140) uniform modelData {
float vpwidth;
float vpheight;
bool team_glow_enabled;
int team_glow_enabled;
float znear;
float zfar;
@@ -86,9 +91,16 @@ layout (std140) uniform modelData {
int flags;
};
layout (std140) uniform shadowCascadeParams {
#ifdef VULKAN
layout(set = 0, binding = 6, std140)
#else
layout(std140)
#endif
uniform shadowCascadeParams {
int cascade_offset;
int cascade_count;
float rtShadowBiasMin;
float rtShadowBiasMax;
mat4 shadow_mv_matrix;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
@@ -96,16 +108,28 @@ layout (std140) uniform shadowCascadeParams {
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
};
in VertexOutput {
mat3 tangentMatrix;
#ifdef VULKAN
layout(set = 1, binding = 1) uniform sampler2DArray materialTextures[16];
#define sBasemap materialTextures[0]
#define sGlowmap materialTextures[1]
#define sSpecmap materialTextures[2]
#define sNormalmap materialTextures[3]
#define sAmbientmap materialTextures[5]
#define sMiscmap materialTextures[6]
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
float fogDist;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
layout(set = 0, binding = 2) uniform sampler2DArrayShadow shadow_map;
layout(set = 1, binding = 5) uniform sampler2D sFramebuffer;
#ifdef MODEL_SDR_FLAG_RT_SHADOWS
layout(set = 0, binding = 5) uniform accelerationStructureEXT shadow_tlas;
#endif
layout(location = 0) in VertexOutput {
vec4 position;
vec3 normal;
vec4 texCoord;
mat3 tangentMatrix;
float fogDist;
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
vec4 shadowUV[NUM_SHADOW_CASCADES];
@@ -113,6 +137,12 @@ in VertexOutput {
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
} vertIn;
layout(location = 0) out vec4 fragOut0;
layout(location = 1) out vec4 fragOut1;
layout(location = 2) out vec4 fragOut2;
layout(location = 3) out vec4 fragOut3;
layout(location = 4) out vec4 fragOut4;
#else
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_DIFFUSE
uniform sampler2DArray sBasemap;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_DIFFUSE
@@ -136,16 +166,33 @@ uniform sampler2DArray sMiscmap;
uniform sampler2DArrayShadow shadow_map;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
out vec4 fragOut0;
in VertexOutput {
mat3 tangentMatrix;
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
float fogDist;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_FOG
vec4 position;
vec3 normal;
vec4 texCoord;
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
vec4 shadowUV[NUM_SHADOW_CASCADES];
vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
} vertIn;
out vec4 fragOut0;
out vec4 fragOut1;
out vec4 fragOut2;
out vec4 fragOut3;
out vec4 fragOut4;
#endif
vec3 FresnelLazarovEnv(vec3 specColor, vec3 view, vec3 normal, float gloss)
{
// Fresnel for environment lighting
// Fresnel for environment lighting
// Equation referenced from Dimitar Lazarov's presentation titled Physically Based Rendering in Call of Duty: Black Ops
return specColor + (vec3(1.0) - specColor) * pow(1.0 - clamp(dot(view, normal), 0.0, 1.0), 5.0) / (4.0 - 3.0 * gloss);
}
@@ -180,11 +227,34 @@ vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial,
vec3 lightAmbient = ambientFactor * aoFactor;
vec3 lightDiffuse = vec3(0.0, 0.0, 0.0);
vec3 lightSpecular = vec3(0.0, 0.0, 0.0);
#ifdef MODEL_SDR_FLAG_RT_SHADOWS
// Whenever this compile-time flag is present we're always in Vulkan/large-shader
// mode, so `flags`/MODEL_SDR_FLAG_SHADOWS (the runtime shadow-receiving gate that
// IF_FLAG expands to elsewhere in this file) are guaranteed to exist here too.
bool rtShadowsActive = (flags & MODEL_SDR_FLAG_SHADOWS) != 0;
mat4 invView = inverse(viewMatrix);
vec3 worldPos = (invView * vertIn.position).xyz;
vec3 worldNormal = normalize((invView * vec4(normal, 0.0)).xyz);
int shadowedDirectionalCount = 0;
#endif
#pragma optionNV unroll all
for (int i = 0; i < n_lights; ++i) {
#ifdef MODEL_SDR_FLAG_RT_SHADOWS
if (rtShadowsActive && lights[i].light_type == LT_DIRECTIONAL && shadowedDirectionalCount < MAX_RT_SHADOW_LIGHTS) {
vec3 worldSunDir = normalize((invView * vec4(lights[i].position.xyz, 0.0)).xyz);
float rtShadowBias = computeRtShadowBias(length(vertIn.position.xyz), rtShadowBiasMin, rtShadowBiasMax);
shadow = traceShadowRay(shadow_tlas, worldPos, worldNormal, worldSunDir, RT_SHADOW_MAX_DISTANCE, rtShadowBias);
++shadowedDirectionalCount;
} else {
shadow = 1.0;
}
#else
if(i > 0) {
shadow = 1.0;
}
#endif
float roughness = clamp(1.0f - gloss, 0.0f, 1.0f);
float alpha = roughness * roughness;
vec3 lightDir;
@@ -207,7 +277,6 @@ void main()
// setup our baseline values for base, emissive, fresnel, gloss, AO and normal
// default specular value is set below, as it depends on the diffuse color
vec4 baseColor = color;
vec4 emissiveColor = vec4(0.0, 0.0, 0.0, 1.0);
float fresnelFactor = 0.0;
@@ -216,6 +285,7 @@ void main()
vec3 unitNormal = normalize(vertIn.normal);
vec3 normal = unitNormal;
// Ambient occlusion map
#prereplace IF_FLAG MODEL_SDR_FLAG_AMBIENT
// red channel is ambient occlusion factor which only affects ambient lighting.
// green is cavity occlusion factor which only affects diffuse and specular lighting.
@@ -242,6 +312,7 @@ void main()
distort = vec2(cos(vertIn.position.x*vertIn.position.w*0.005+anim_timer*20.0)*sin(vertIn.position.y*vertIn.position.w*0.005),sin(vertIn.position.x*vertIn.position.w*0.005+anim_timer*20.0)*cos(vertIn.position.y*vertIn.position.w*0.005))*0.03;
}
// Diffuse map
#prereplace IF_FLAG MODEL_SDR_FLAG_DIFFUSE
vec2 diffuseTexCoord = texCoord;
if (effect_num == 2) {
@@ -249,7 +320,7 @@ void main()
}
baseColor = texture(sBasemap, vec3(diffuseTexCoord, float(sBasemapIndex)));
#prereplace IF_FLAG MODEL_SDR_FLAG_HDR
#prereplace IF_FLAG MODEL_SDR_FLAG_HDR
baseColor.rgb = srgb_to_linear(baseColor.rgb);
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_HDR
@@ -265,7 +336,7 @@ void main()
// Anti-glint "trick" based on Valve's "Advanced VR Rendering" talk at GDC2015:
// http://media.steampowered.com/apps/valve/2015/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf Page 43
// basically make surfaces rougher if local normals change too fast in screenspace
// basically make surfaces rougher if local normals change too fast in screenspace
vec2 normDx = dFdx(unitNormal.xy);
vec2 normDy = dFdy(unitNormal.xy);
float glossGeo = 1.0f - pow(clamp(max(dot(normDx,normDx), dot(normDy,normDy)),0.0,1.0),0.33);
@@ -273,19 +344,20 @@ void main()
// Now that we have a base color and min gloss value, compute the spec color
vec4 specColor = vec4(baseColor.rgb * SPEC_FACTOR_NO_SPEC_MAP, glossData);
// Specular map
#prereplace IF_FLAG MODEL_SDR_FLAG_SPEC
specColor = texture(sSpecmap, vec3(texCoord, float(sSpecmapIndex)));
#prereplace IF_FLAG MODEL_SDR_FLAG_ALPHA_MULT
specColor *= alphaMult;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_ALPHA_MULT
if(alphaGloss) glossData = specColor.a;
if(gammaSpec) {
specColor.rgb = max(specColor.rgb, vec3(0.03f)); // hardcoded minimum specular value. read John Hable's blog post titled 'Everything Is Shiny'.
if(alphaGloss != 0) glossData = specColor.a;
if(gammaSpec != 0) {
specColor.rgb = max(specColor.rgb, vec3(0.03f)); // hardcoded minimum specular value. read John Hable's blog post titled 'Everything Is Shiny'.
fresnelFactor = 1.0;
}
#prereplace IF_FLAG MODEL_SDR_FLAG_HDR
specColor.rgb = srgb_to_linear(specColor.rgb);
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_HDR
@@ -297,11 +369,12 @@ void main()
vec4 teamMask = vec4(0.0);
vec3 team_color_glow = vec3(0.0);
// Misc map / team colors
#prereplace IF_FLAG MODEL_SDR_FLAG_MISC
#prereplace IF_FLAG MODEL_SDR_FLAG_TEAMCOLOR
teamMask = texture(sMiscmap, vec3(texCoord, float(sMiscmapIndex)));
//For team colors applied to a diffuse or spec map, we assume that the base color of the diffuse
//For team colors applied to a diffuse or spec map, we assume that the base color of the diffuse
//at this point is vec3(0.5). To get accurate results, we subtract 0.5 from the team colors
vec3 color_offset = vec3(-0.5) * (teamMask.x + teamMask.y);
@@ -313,11 +386,11 @@ void main()
specColor.rgb = linear_to_srgb(specColor.rgb);
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_HDR
baseColor.rgb += team_color;
baseColor.rgb += team_color;
baseColor.rgb = max(baseColor.rgb, vec3(0.0)); // We need to make sure that nothing here ever goes negative
specColor.rgb += team_color;
specColor.rgb = max(specColor.rgb, vec3(0.03));
#prereplace IF_FLAG MODEL_SDR_FLAG_HDR
baseColor.rgb = srgb_to_linear(baseColor.rgb);
specColor.rgb = srgb_to_linear(specColor.rgb);
@@ -330,7 +403,12 @@ void main()
#prereplace IF_FLAG MODEL_SDR_FLAG_LIGHT
float shadow = 1.0;
#prereplace IF_FLAG MODEL_SDR_FLAG_SHADOWS
// Raytraced shadows are computed per-light (for every directional light,
// up to MAX_RT_SHADOW_LIGHTS) inside CalculateLighting()'s loop instead --
// this single shared `shadow` value only matters for the CSM fallback below.
#ifndef MODEL_SDR_FLAG_RT_SHADOWS
shadow = getShadowValue(shadow_map, -vertIn.position.z, vertIn.shadowUV, cascade_distances, smoothness_factors, cascade_offset, cascade_count);
#endif
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS
baseColor.rgb = CalculateLighting(normal, baseColor.rgb, specColor.rgb, glossData, fresnelFactor, shadow, aoFactors.x);
#prereplace ELSE_FLAG //MODEL_SDR_FLAG_LIGHT
@@ -340,12 +418,13 @@ void main()
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_LIGHT
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DEFERRED
// Glow map
#prereplace IF_FLAG MODEL_SDR_FLAG_GLOW
vec3 glowColor = texture(sGlowmap, vec3(texCoord, float(sGlowmapIndex))).rgb;
#prereplace IF_FLAG MODEL_SDR_FLAG_MISC
#prereplace IF_FLAG MODEL_SDR_FLAG_TEAMCOLOR
float glowColorLuminance = dot(glowColor, vec3(0.299, 0.587, 0.114));
glowColor = team_glow_enabled ? mix(max(team_color_glow, vec3(0.0)), glowColor, clamp(glowColorLuminance - teamMask.b - teamMask.a, 0.0, 1.0)) : glowColor;
glowColor = (team_glow_enabled != 0) ? mix(max(team_color_glow, vec3(0.0)), glowColor, clamp(glowColorLuminance - teamMask.b - teamMask.a, 0.0, 1.0)) : glowColor;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_TEAMCOLOR
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_MISC
#prereplace IF_FLAG MODEL_SDR_FLAG_HDR
@@ -375,12 +454,14 @@ void main()
specColor.rgb *= fogDensityFinal;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_FOG
// Desaturation
#prereplace IF_FLAG MODEL_SDR_FLAG_DIFFUSE
if(desaturate == 1) {
baseColor.rgb = color.rgb * dot(vec3(1.0), baseColor.rgb) * 0.3333333;
}
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DIFFUSE
// Ship effects
if (effect_num == 0) {
float shinefactor = 1.0/(1.0 + pow(abs((fract(abs(texCoord.x))-anim_timer) * 1000.0), 2.0)) * 1000.0;
emissiveColor.rgb += vec3(shinefactor);
@@ -427,5 +508,11 @@ void main()
fragOut2 = vec4(normal, glossData);
fragOut3 = vec4(specColor.rgb, fresnelFactor);
fragOut4 = emissiveColor;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DEFERRED
}
#prereplace ELSE_FLAG //MODEL_SDR_FLAG_DEFERRED
// Vulkan has undefined values for unwritten outputs; zero-init G-buffer outputs
fragOut1 = vec4(0.0);
fragOut2 = vec4(0.0);
fragOut3 = vec4(0.0);
fragOut4 = vec4(0.0);
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DEFERRED
}
+6 -6
View File
@@ -41,7 +41,7 @@ layout (std140) uniform modelData {
vec4 clip_equation;
float thruster_scale;
bool use_clip_plane;
int use_clip_plane;
int n_lights;
float defaultGloss;
@@ -53,10 +53,10 @@ layout (std140) uniform modelData {
vec3 emissionFactor;
bool alphaGloss;
int alphaGloss;
bool gammaSpec;
bool envGloss;
int gammaSpec;
int envGloss;
int effect_num;
int sBasemapIndex;
@@ -69,7 +69,7 @@ layout (std140) uniform modelData {
float vpwidth;
float vpheight;
bool team_glow_enabled;
int team_glow_enabled;
float znear;
float zfar;
@@ -165,7 +165,7 @@ out VertexOutput {
vertOut.shadowPos = vertIn[vert].shadowPos;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS
if (use_clip_plane) {
if (use_clip_plane != 0) {
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
}
EmitVertex();
+70 -11
View File
@@ -1,16 +1,27 @@
#ifndef VULKAN
//? #version 150
#extension GL_ARB_gpu_shader5: enable
#endif
#include "shadows.sdr"
#conditional_include +"LARGE_SHADER" "main_large.sdr"
#conditional_include -"LARGE_SHADER" "main_small.sdr"
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
layout(location = 1) in vec4 vertColor;
layout(location = 2) in vec4 vertTexCoord;
layout(location = 3) in vec3 vertNormal;
layout(location = 4) in vec4 vertTangent;
layout(location = 5) in float vertModelID;
#else
in vec4 vertPosition;
in vec4 vertTexCoord;
in vec3 vertNormal;
in vec4 vertTangent;
in float vertModelID;
#endif
#define MAX_LIGHTS 8
@@ -26,7 +37,12 @@ struct model_light {
float ml_sourceRadius;
};
layout (std140) uniform modelData {
#ifdef VULKAN
layout(set = 1, binding = 0, std140)
#else
layout(std140)
#endif
uniform modelData {
mat4 modelViewMatrix;
mat4 modelMatrix;
mat4 viewMatrix;
@@ -45,7 +61,7 @@ layout (std140) uniform modelData {
vec4 clip_equation;
float thruster_scale;
bool use_clip_plane;
int use_clip_plane;
int n_lights;
float defaultGloss;
@@ -57,10 +73,10 @@ layout (std140) uniform modelData {
vec3 emissionFactor;
bool alphaGloss;
int alphaGloss;
bool gammaSpec;
bool envGloss;
int gammaSpec;
int envGloss;
int effect_num;
int sBasemapIndex;
@@ -73,7 +89,7 @@ layout (std140) uniform modelData {
float vpwidth;
float vpheight;
bool team_glow_enabled;
int team_glow_enabled;
float znear;
float zfar;
@@ -89,9 +105,16 @@ layout (std140) uniform modelData {
int flags;
};
layout (std140) uniform shadowCascadeParams {
#ifdef VULKAN
layout(set = 0, binding = 6, std140)
#else
layout(std140)
#endif
uniform shadowCascadeParams {
int cascade_offset;
int cascade_count;
float rtShadowBiasMin;
float rtShadowBiasMax;
mat4 shadow_mv_matrix;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
@@ -99,6 +122,23 @@ layout (std140) uniform shadowCascadeParams {
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
};
#ifdef VULKAN
// Transform buffer for batched submodel rendering
layout(set = 1, binding = 3, std430) readonly buffer TransformBuffer {
mat4 transforms[];
} transformBuf;
// Outputs to fragment shader
layout(location = 0) out VertexOutput {
vec4 position;
vec3 normal;
vec4 texCoord;
mat3 tangentMatrix;
float fogDist;
vec4 shadowUV[NUM_SHADOW_CASCADES];
vec4 shadowPos;
} vertOut;
#else
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM
uniform samplerBuffer transform_tex;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM
@@ -132,6 +172,7 @@ void getModelTransform(inout mat4 transform, out bool invisible, int id, int mat
transform[3].w = 1.0;
}
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM
#endif
void main()
{
@@ -139,20 +180,30 @@ void main()
vec3 normal;
vec4 texCoord;
mat4 orient = mat4(1.0);
mat4 scale = mat4(1.0);
bool clipModel = false;
// Transform loading
#prereplace IF_FLAG MODEL_SDR_FLAG_TRANSFORM
#ifdef VULKAN
int id = int(vertModelID);
orient = transformBuf.transforms[buffer_matrix_offset + id];
clipModel = (orient[3].w >= 0.9);
orient[3].w = 1.0;
#else
getModelTransform(orient, clipModel, int(vertModelID), buffer_matrix_offset);
#endif
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_TRANSFORM
texCoord = textureMatrix * vertTexCoord;
vec4 vertex = vertPosition;
// Thruster scale
#prereplace IF_FLAG MODEL_SDR_FLAG_THRUSTER
if(vertex.z < -1.5) {
vertex.z *= thruster_scale;
}
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_THRUSTER
// Transform the normal into eye space and normalize the result.
normal = normalize(mat3(modelViewMatrix) * mat3(orient) * vertNormal);
position = modelViewMatrix * orient * vertex;
@@ -170,10 +221,17 @@ void main()
vec3 b = cross(normal, t) * vertTangent.w;
vertOut.tangentMatrix = mat3(t, b, normal);
// Fog distance
#prereplace IF_FLAG MODEL_SDR_FLAG_FOG
vertOut.fogDist = gl_Position.z - fogNear;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_FOG
#ifdef VULKAN
#prereplace IF_NOT_FLAG MODEL_SDR_FLAG_FOG
vertOut.fogDist = 0.0;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_FOG
#endif
// Clip invisible (blown-off) submodels
#prereplace IF_FLAG MODEL_SDR_FLAG_TRANSFORM
if (clipModel) {
// Clip this model by moving all vertices outside the clip volume
@@ -181,7 +239,8 @@ void main()
}
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_TRANSFORM
if(use_clip_plane) {
// Clip plane
if(use_clip_plane != 0) {
gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex);
} else {
gl_ClipDistance[0] = 1.0;
@@ -27,8 +27,14 @@ SDR_FLAG(MODEL_SDR_FLAG_ALPHA_MULT , (1 << 14), false)
#ifndef MODEL_SDR_FLAG_MODE_GLSL
//The following ones are used ONLY as compile-time flags, but they still need to be defined here to ensure no conflict occurs
//But since these are checked with ifdefs even for the large shader, they must never be available in GLSL mode
SDR_FLAG(MODEL_SDR_FLAG_SHADOW_MAP , (1 << 15), true)
SDR_FLAG(MODEL_SDR_FLAG_THICK_OUTLINES, (1 << 16), true)
// Like MODEL_SDR_FLAG_SHADOW_MAP/THICK_OUTLINES above, this must stay a genuine
// compile-time ifdef check (not the runtime flag-branch macros used for ordinary
// MODEL_SDR_FLAG_* bits) since Vulkan always runs in "large shader" mode, where
// those bits are only checked via a runtime `flags` uniform -- ray query usage
// can't be gated that way, since the GL_EXT_ray_query extension and its types
// must not appear at all in a shader compiled for hardware that doesn't support it.
SDR_FLAG(MODEL_SDR_FLAG_RT_SHADOWS , (1 << 17), true)
#endif
+37 -2
View File
@@ -1,5 +1,27 @@
#include "z-compress.sdr"
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(location = 1) out vec4 fragOut1;
layout(location = 2) out vec4 fragOut2;
layout(location = 3) out vec4 fragOut3;
layout(location = 4) out vec4 fragOut4;
layout(set = 1, binding = 1) uniform sampler2DMS msaaTex[6];
#define texColor msaaTex[0]
#define texPos msaaTex[1]
#define texNormal msaaTex[2]
#define texSpecular msaaTex[3]
#define texEmissive msaaTex[4]
#define texDepth msaaTex[5]
layout(std140, set = 2, binding = 0) uniform genericData {
int samples;
float fov;
};
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
out vec4 fragOut1;
@@ -19,17 +41,20 @@ layout (std140) uniform genericData {
int samples;
float fov;
};
#endif
const float voxelDepth = 2.5f;
const float voxelDepthFalloff = 2.5f;
#define SORT(a, b) if (a > b) { float t = dists[a]; dists[a] = dists[b]; dists[b] = t; }
#define SORT(a, b) if (dists[a] > dists[b]) { float t = dists[a]; dists[a] = dists[b]; dists[b] = t; }
//The following sorting networks for median calculation are NOT complete. Calculations that don't affect the
//median entry are removed, and thus, while the median element is correct, the rest of the array is NOT fully sorted
#ifdef SAMPLES_4
#define SAMPLE_CNT 4
float getMedianDist(ivec2 texel) {
float dists[4];
@@ -49,6 +74,8 @@ float getMedianDist(ivec2 texel) {
#else
#ifdef SAMPLES_8
#define SAMPLE_CNT 8
float getMedianDist(ivec2 texel) {
float dists[8];
@@ -80,6 +107,8 @@ float getMedianDist(ivec2 texel) {
#else
#ifdef SAMPLES_16
#define SAMPLE_CNT 16
float getMedianDist(ivec2 texel) {
float dists[16];
@@ -143,6 +172,8 @@ float getMedianDist(ivec2 texel) {
#else
#define SAMPLE_CNT samples
//Fallback, do front first
float getMedianDist(ivec2 texel) {
float minDist = -1000000;
@@ -160,7 +191,11 @@ float getMedianDist(ivec2 texel) {
void main()
{
vec2 texSize = textureSize(texColor);
#ifdef VULKAN
ivec2 texel = ivec2(texSize * fragTexCoord);
#else
ivec2 texel = ivec2(texSize * fragTexCoord.xy);
#endif
float texelWidthFactor = tan(fov / texSize.y);
float dist = getMedianDist(texel);
@@ -173,7 +208,7 @@ void main()
vec4 emissive = vec4(0);
float depth = 0;
for(int i = 0; i < samples; i++) {
for(int i = 0; i < SAMPLE_CNT; i++) {
vec4 localPos = texelFetch(texPos, texel, i);
localPos.z = uncompress_depth_value(localPos.z);
//Calculate local weight from distance Voxel, but if the distance is 0 (i.e. no model at all), set weight to 1 to allow stuff like background emissive
+32 -25
View File
@@ -1,37 +1,44 @@
#ifdef VULKAN
layout (location = 0) in vec2 ftcoord;
layout (location = 1) in vec2 fpos;
layout (location = 0) out vec4 outColor;
layout (set = 1, binding = 1) uniform sampler2DArray nvg_tex;
#else
// Enabled at all times but the define is kept here in case this should be configurable in the future
#define EDGE_AA
layout(std140) uniform NanoVGUniformData {
mat3 scissorMat;
mat3 paintMat;
vec4 innerCol;
vec4 outerCol;
vec2 scissorExt;
vec2 scissorScale;
vec2 extent;
float radius;
float feather;
float strokeMult;
float strokeThr;
int texType;
int type;
vec2 viewSize;
int texArrayIndex;
};
uniform sampler2DArray nvg_tex;
in vec2 ftcoord;
in vec2 fpos;
out vec4 outColor;
#endif
#ifdef VULKAN
layout (set = 2, binding = 2, std140)
#else
layout(std140)
#endif
uniform NanoVGUniformData {
mat3 scissorMat;
mat3 paintMat;
vec4 innerCol;
vec4 outerCol;
vec2 scissorExt;
vec2 scissorScale;
vec2 extent;
float radius;
float feather;
float strokeMult;
float strokeThr;
int texType;
int type;
vec2 viewSize;
int texArrayIndex;
};
float sdroundrect(vec2 pt, vec2 ext, float rad) {
vec2 ext2 = ext - vec2(rad,rad);
+28 -7
View File
@@ -1,5 +1,24 @@
layout(std140) uniform NanoVGUniformData {
#ifdef VULKAN
layout (location = 0) in vec4 vertPosition;
layout (location = 2) in vec4 vertTexCoord;
layout (location = 0) out vec2 ftcoord;
layout (location = 1) out vec2 fpos;
#else
in vec2 vertPosition;
in vec2 vertTexCoord;
out vec2 ftcoord;
out vec2 fpos;
#endif
#ifdef VULKAN
layout (set = 2, binding = 2, std140)
#else
layout(std140)
#endif
uniform NanoVGUniformData {
mat3 scissorMat;
mat3 paintMat;
@@ -24,14 +43,16 @@ layout(std140) uniform NanoVGUniformData {
int texArrayIndex;
};
in vec2 vertPosition;
in vec2 vertTexCoord;
out vec2 ftcoord;
out vec2 fpos;
void main(void) {
#ifdef VULKAN
ftcoord = vertTexCoord.xy;
fpos = vertPosition.xy;
gl_Position = vec4(2.0 * vertPosition.x / viewSize.x - 1.0,
1.0 - 2.0 * vertPosition.y / viewSize.y,
0.0, 1.0);
#else
ftcoord = vertTexCoord;
fpos = vertPosition;
gl_Position = vec4(2.0*vertPosition.x/viewSize.x - 1.0, 1.0 - 2.0*vertPosition.y/viewSize.y, 0, 1);
#endif
}
+14 -4
View File
@@ -1,11 +1,21 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
layout(set = 1, binding = 4) uniform sampler2D depth_tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
uniform sampler2D depth_tex;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
float timer;
float noise_amount;
float saturation;
@@ -19,7 +29,7 @@ layout (std140) uniform genericData {
vec3 tint;
float dither;
// these are blank, valid slots for modders to create custom effects
// these are blank, valid slots for modders to create custom effects
// that can be defined in post_processing.tbl and coded below
vec3 custom_effect_vec3_a;
float custom_effect_float_a;
+11
View File
@@ -1,8 +1,19 @@
#ifdef VULKAN
layout(location = 0) out vec2 fragTexCoord;
#else
in vec4 vertPosition;
in vec4 vertTexCoord;
out vec4 fragTexCoord;
#endif
void main()
{
#ifdef VULKAN
vec2 pos = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
fragTexCoord = pos;
gl_Position = vec4(pos * 2.0 - 1.0, 0.0, 1.0);
#else
fragTexCoord = vertTexCoord;
gl_Position = vertPosition;
#endif
}
+18 -3
View File
@@ -1,4 +1,13 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec4 fragColor;
layout(location = 2) in vec2 fragScreenPosition;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray baseMap;
#else
in vec2 fragTexCoord;
in vec4 fragColor;
in vec2 fragScreenPosition;
@@ -6,12 +15,18 @@ in vec2 fragScreenPosition;
out vec4 fragOut0;
uniform sampler2DArray baseMap;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(std140, set = 2, binding = 0)
#else
layout(std140)
#endif
uniform genericData {
mat4 projMatrix;
vec2 offset;
bool textured;
int textured;
int baseMapIndex;
float horizontalSwipeOffset;
@@ -25,7 +40,7 @@ void main() {
float distance = horizontalSwipeOffset - fragScreenPosition.x;
vec4 color;
if (textured) {
if (textured != 0) {
color = texture(baseMap, vec3(fragTexCoord, float(baseMapIndex))) * fragColor;
} else {
color = fragColor;
+17 -2
View File
@@ -1,4 +1,13 @@
#ifdef VULKAN
layout(location = 0) in vec2 vertPosition;
layout(location = 1) in vec4 vertColor;
layout(location = 2) in vec2 vertTexCoord;
layout(location = 0) out vec2 fragTexCoord;
layout(location = 1) out vec4 fragColor;
layout(location = 2) out vec2 fragScreenPosition;
#else
in vec2 vertPosition;
in vec4 vertColor;
in vec2 vertTexCoord;
@@ -6,12 +15,18 @@ in vec2 vertTexCoord;
out vec2 fragTexCoord;
out vec4 fragColor;
out vec2 fragScreenPosition;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(std140, set = 2, binding = 0)
#else
layout(std140)
#endif
uniform genericData {
mat4 projMatrix;
vec2 offset;
bool textured;
int textured;
int baseMapIndex;
float horizontalSwipeOffset;
+55 -12
View File
@@ -5,23 +5,43 @@
#extension GL_ARB_shader_viewport_layer_array : enable
#endif
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
layout(location = 2) in vec4 vertTexCoord;
layout(location = 3) in vec3 vertNormal;
layout(location = 4) in vec4 vertTangent;
layout(location = 5) in float vertModelID;
#else
in vec4 vertPosition;
in vec4 vertTexCoord;
in vec3 vertNormal;
in vec4 vertTangent;
in float vertModelID;
#endif
layout (std140) uniform shadowMapData {
#ifdef VULKAN
layout(set = 1, binding = 7, std140)
#else
layout(std140)
#endif
uniform shadowMapData {
mat4 modelViewMatrix;
mat4 modelMatrix;
vec4 clip_equation;
bool use_clip_plane;
int use_clip_plane;
int buffer_matrix_offset;
};
layout (std140) uniform shadowCascadeParams {
#ifdef VULKAN
layout(set = 0, binding = 6, std140)
#else
layout(std140)
#endif
uniform shadowCascadeParams {
int cascade_offset;
int cascade_count;
float rtShadowBiasMin;
float rtShadowBiasMax;
mat4 shadow_mv_matrix;
mat4 shadow_proj_matrix[NUM_SHADOW_CASCADES];
@@ -29,8 +49,17 @@ layout (std140) uniform shadowCascadeParams {
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
};
#ifdef VULKAN
// Transform buffer for batched submodel rendering (matches main-v.sdr)
layout(set = 1, binding = 3, std430) readonly buffer TransformBuffer {
mat4 transforms[];
} transformBuf;
#else
uniform samplerBuffer transform_tex;
#endif
// GEOMETRY_FALLBACK is only ever requested on the OpenGL backend (Vulkan always
// supports writing gl_Layer from the vertex shader, see CAPABILITY_FAST_SHADOWS).
#ifdef GEOMETRY_FALLBACK
out VertexOutput {
#if !defined(GL_ARB_gpu_shader5)
@@ -43,6 +72,7 @@ out VertexOutput {
#define TEXELS_PER_MATRIX 4
#ifndef VULKAN
void getModelTransform(inout mat4 transform, out bool invisible, int id, int matrix_offset)
{
transform[0] = texelFetch(transform_tex, (matrix_offset + id) * TEXELS_PER_MATRIX);
@@ -52,6 +82,7 @@ void getModelTransform(inout mat4 transform, out bool invisible, int id, int mat
invisible = transform[3].w >= 0.9;
transform[3].w = 1.0;
}
#endif
void main()
{
@@ -60,19 +91,31 @@ void main()
mat4 orient = mat4(1.0);
bool clipModel = false;
#ifdef VULKAN
int id = int(vertModelID);
orient = transformBuf.transforms[buffer_matrix_offset + id];
clipModel = (orient[3].w >= 0.9);
orient[3].w = 1.0;
#else
getModelTransform(orient, clipModel, int(vertModelID), buffer_matrix_offset);
#endif
vec4 vertex = vertPosition;
normal = normalize(mat3(modelViewMatrix) * mat3(orient) * vertNormal);
position = modelViewMatrix * orient * vertex;
#if !defined(GL_ARB_gpu_shader5)
#ifdef APPLE
vertOut.instance = float(gl_InstanceIDARB);
#else
vertOut.instance = float(gl_InstanceID);
#endif
#ifdef VULKAN
int instanceIndex = gl_InstanceIndex;
#else
#if !defined(GL_ARB_gpu_shader5)
#ifdef APPLE
vertOut.instance = float(gl_InstanceIDARB);
#else
vertOut.instance = float(gl_InstanceID);
#endif
#endif
int instanceIndex = gl_InstanceID;
#endif
if (clipModel) {
@@ -80,13 +123,13 @@ void main()
}
else {
#ifndef GEOMETRY_FALLBACK
int cascade_id = cascade_offset + gl_InstanceID;
int cascade_id = cascade_offset + instanceIndex;
position = shadow_proj_matrix[cascade_id] * position;
#endif
gl_Position = position;
}
if (use_clip_plane) {
if (use_clip_plane != 0) {
gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex);
} else {
gl_ClipDistance[0] = 1.0;
@@ -96,6 +139,6 @@ void main()
vertOut.normal = normal;
vertOut.clipModel = clipModel ? 1.0 : 0.0;
#else
gl_Layer = cascade_offset + gl_InstanceID;
gl_Layer = cascade_offset + instanceIndex;
#endif
}
+77 -11
View File
@@ -6,17 +6,6 @@
#define SAMPLES_PCSS 1
#endif
vec4 transformToShadowMap(mat4 shadow_proj_matrix, int i, vec4 pos)
{
vec4 shadow_proj;
shadow_proj = shadow_proj_matrix * pos;
shadow_proj += 1.0;
shadow_proj *= 0.5;
shadow_proj.w = shadow_proj.z;
shadow_proj.z = float(i);
return shadow_proj;
}
float samplePoissonPCSS(sampler2DArrayShadow shadow_map, vec4 shadowUV, float maxUVOffset)
{
#if SMOOTH_PCSS
@@ -78,3 +67,80 @@ float getShadowValue(sampler2DArrayShadow shadow_map, float depth, vec4 shadowUV
samplePoissonPCSS(shadow_map, shadowUV[cascade + 1], smoothness_factors[(cascade + 1) / 4][(cascade + 1) % 4]),
smoothstep(cascade_end - dist_threshold, cascade_end, depth));
}
// Raytraced shadow test via inline ray query against the shadow TLAS. Only
// compiled in for the specific shader variants that request it (MODEL shaders
// via MODEL_SDR_FLAG_RT_SHADOWS, deferred lighting via RT_SHADOWS) -- both are
// genuine compile-time #defines (not the runtime `flags` uniform branch that
// other MODEL_SDR_FLAG_* bits use), so GL_EXT_ray_query and its types never
// appear in a shader compiled for hardware that doesn't support them.
// Distance used for lights with no physical extent to shadow against (directional/sun
// lights) -- effectively unbounded relative to any FS2Open mission scale. Defined
// unconditionally (not just under the RT_SHADOWS guard below) since GetLightInfo()
// in deferred-f.sdr/main-f.sdr references it regardless of which shadow method is active.
const float RT_SHADOW_MAX_DISTANCE = 10000.0;
#if defined(MODEL_SDR_FLAG_RT_SHADOWS) || defined(RT_SHADOWS)
#extension GL_EXT_ray_query : require
// Ray origin bias grows with distance from the camera, between the rtShadowBiasMin/
// rtShadowBiasMax bounds (see shadowCascadeParams uniform block, and the Min/Max
// Raytraced Shadow Bias options in shadows.cpp). Empirically, geometry close to the
// camera only needs biasMin worth of offset to clear self-intersection acne, while
// distant geometry needs more -- consistent with the error source being G-buffer/
// rasterization position-reconstruction precision (which degrades with view depth),
// not a fixed float-precision quantity (a prior attempt using the Ray Tracing Gems
// ch.6 "next representable float" technique, which only accounts for the latter,
// made acne *worse* since its offset was far too small for the actual error here).
// cameraDist is view-space distance (i.e. length() of the camera-relative position
// already available at both call sites), not world-space distance from the origin.
const float RT_SHADOW_BIAS_RAMP_DISTANCE = 2000.0;
float computeRtShadowBias(float cameraDist, float biasMin, float biasMax)
{
return mix(biasMin, biasMax, clamp(cameraDist / RT_SHADOW_BIAS_RAMP_DISTANCE, 0.0, 1.0));
}
// worldPos/worldNormal/worldLightDir must all be in the same world space the
// shadow TLAS was built in (see VulkanRaytracingManager::buildTlas()) -- i.e.
// FS2Open object-space positions/orientations, not view/eye-space. worldLightDir
// must point FROM the surface TOWARD the light.
//
// tMax must be the actual distance to the light (RT_SHADOW_MAX_DISTANCE for
// directional lights, which have no real distance). For point/tube/cone lights this
// is a correctness requirement, not just a cost bound: without it, geometry sitting
// behind the light (farther from the surface than the light itself) would register
// as a hit and incorrectly shadow the fragment, since a light can't be occluded by
// something it doesn't reach.
//
// bias is a world-unit offset applied along worldNormal before tracing, to clear
// the source triangle -- see computeRtShadowBias() above for how callers derive it.
float traceShadowRay(accelerationStructureEXT tlas, vec3 worldPos, vec3 worldNormal, vec3 worldLightDir, float tMax, float bias)
{
vec3 origin = worldPos + worldNormal * bias;
vec3 direction = normalize(worldLightDir);
rayQueryEXT rq;
rayQueryInitializeEXT(rq, tlas, gl_RayFlagsOpaqueEXT | gl_RayFlagsTerminateOnFirstHitEXT,
0xFF, origin, 0.001, direction, tMax);
while (rayQueryProceedEXT(rq)) {}
return (rayQueryGetIntersectionTypeEXT(rq, true) == gl_RayQueryCommittedIntersectionNoneEXT) ? 1.0 : 0.0;
}
#endif
vec4 transformToShadowMap(mat4 shadow_proj_matrix, int i, vec4 pos)
{
vec4 shadow_proj;
shadow_proj = shadow_proj_matrix * pos;
#ifdef VULKAN
// Vulkan shadow projection: XY is [-1,1] → [0,1], Z is already [0,1]
shadow_proj.xy = shadow_proj.xy * 0.5 + 0.5;
#else
// OpenGL: all components [-1,1] → [0,1]
shadow_proj += 1.0;
shadow_proj *= 0.5;
#endif
shadow_proj.w = shadow_proj.z;
shadow_proj.z = float(i);
return shadow_proj;
}
@@ -3,14 +3,28 @@
const float EMISSIVE_GAIN = 2.0;
#ifdef VULKAN
layout(location = 0) in vec4 fragImpactUV;
layout(location = 1) in float fragNormOffset;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray shieldMap;
#else
in vec4 fragImpactUV;
in float fragNormOffset;
out vec4 fragOut0;
uniform sampler2DArray shieldMap;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
mat4 shieldModelViewMatrix;
mat4 shieldProjMatrix;
@@ -1,17 +1,34 @@
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
layout(location = 3) in vec3 vertNormal;
layout(location = 0) out vec4 fragImpactUV;
layout(location = 1) out float fragNormOffset;
#else
in vec4 vertPosition;
in vec3 vertNormal;
out vec4 fragImpactUV;
out float fragNormOffset;
#endif
layout (std140) uniform matrixData {
#ifdef VULKAN
layout(set = 2, binding = 1, std140)
#else
layout(std140)
#endif
uniform matrixData {
mat4 modelViewMatrix;
mat4 projMatrix;
};
layout (std140) uniform genericData {
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
mat4 shieldModelViewMatrix;
mat4 shieldProjMatrix;
@@ -26,7 +43,6 @@ layout (std140) uniform genericData {
void main()
{
gl_Position = projMatrix * modelViewMatrix * vertPosition;
//vec3 normal = normalize(mat3(modelViewMatrix) * vertNormal);
fragNormOffset = dot(hitNormal, vertNormal);
fragImpactUV = shieldProjMatrix * shieldModelViewMatrix * vertPosition;
fragImpactUV += 1.0f;
@@ -1,7 +1,13 @@
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
vec2 smaa_rt_metrics;
};
#else
layout (std140) uniform genericData {
vec2 smaa_rt_metrics;
};
#endif
#define SMAA_RT_METRICS vec4(1.0 / smaa_rt_metrics, smaa_rt_metrics)
@@ -10,6 +16,19 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec2 fragPixcoord;
layout(location = 2) in vec4 fragOffset[3];
layout(location = 0) out vec4 fragOut0;
// [0]=edgesTex, [1]=areaTex, [2]=searchTex
layout(set = 1, binding = 1) uniform sampler2D smaaTex[3];
#define edgesTex smaaTex[0]
#define areaTex smaaTex[1]
#define searchTex smaaTex[2]
#else
in vec2 fragTexCoord;
in vec2 fragPixcoord;
in vec4 fragOffset[3];
@@ -19,6 +38,7 @@ out vec4 fragOut0;
uniform sampler2D edgesTex;
uniform sampler2D areaTex;
uniform sampler2D searchTex;
#endif
void main() {
fragOut0 = SMAABlendingWeightCalculationPS(fragTexCoord, fragPixcoord, fragOffset, edgesTex, areaTex, searchTex, vec4(0.0));
@@ -1,7 +1,13 @@
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
vec2 smaa_rt_metrics;
};
#else
layout (std140) uniform genericData {
vec2 smaa_rt_metrics;
};
#endif
#define SMAA_RT_METRICS vec4(1.0 / smaa_rt_metrics, smaa_rt_metrics)
@@ -10,6 +16,18 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
#ifdef VULKAN
layout(location = 0) out vec2 fragTexCoord;
layout(location = 1) out vec2 fragPixcoord;
layout(location = 2) out vec4 fragOffset[3];
void main() {
vec2 uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
fragTexCoord = uv;
gl_Position = vec4(uv * 2.0 - 1.0, 0.0, 1.0);
SMAABlendingWeightCalculationVS(uv, fragPixcoord, fragOffset);
}
#else
in vec4 vertPosition;
in vec2 vertTexCoord;
@@ -22,3 +40,4 @@ void main() {
fragTexCoord = vertTexCoord;
SMAABlendingWeightCalculationVS(vertTexCoord, fragPixcoord, fragOffset);
}
#endif
@@ -1,7 +1,13 @@
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
vec2 smaa_rt_metrics;
};
#else
layout (std140) uniform genericData {
vec2 smaa_rt_metrics;
};
#endif
#define SMAA_RT_METRICS vec4(1.0 / smaa_rt_metrics, smaa_rt_metrics)
@@ -10,12 +16,21 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec4 fragOffset[3];
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D colorTex;
#else
in vec2 fragTexCoord;
in vec4 fragOffset[3];
out vec4 fragOut0;
uniform sampler2D colorTex;
#endif
void main() {
fragOut0 = vec4(SMAALumaEdgeDetectionPS(fragTexCoord, fragOffset, colorTex), 0.0, 1.0);
@@ -1,7 +1,13 @@
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
vec2 smaa_rt_metrics;
};
#else
layout (std140) uniform genericData {
vec2 smaa_rt_metrics;
};
#endif
#define SMAA_RT_METRICS vec4(1.0 / smaa_rt_metrics, smaa_rt_metrics)
@@ -10,6 +16,17 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
#ifdef VULKAN
layout(location = 0) out vec2 fragTexCoord;
layout(location = 1) out vec4 fragOffset[3];
void main() {
vec2 uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
fragTexCoord = uv;
gl_Position = vec4(uv * 2.0 - 1.0, 0.0, 1.0);
SMAAEdgeDetectionVS(uv, fragOffset);
}
#else
in vec4 vertPosition;
in vec2 vertTexCoord;
@@ -21,3 +38,4 @@ void main() {
fragTexCoord = vertTexCoord;
SMAAEdgeDetectionVS(vertTexCoord, fragOffset);
}
#endif
@@ -1,7 +1,13 @@
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
vec2 smaa_rt_metrics;
};
#else
layout (std140) uniform genericData {
vec2 smaa_rt_metrics;
};
#endif
#define SMAA_RT_METRICS vec4(1.0 / smaa_rt_metrics, smaa_rt_metrics)
@@ -10,6 +16,17 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec4 fragOffset;
layout(location = 0) out vec4 fragOut0;
// [0]=colorTex, [1]=blendTex
layout(set = 1, binding = 1) uniform sampler2D smaaTex[2];
#define colorTex smaaTex[0]
#define blendTex smaaTex[1]
#else
in vec2 fragTexCoord;
in vec4 fragOffset;
@@ -17,6 +34,7 @@ out vec4 fragOut0;
uniform sampler2D colorTex;
uniform sampler2D blendTex;
#endif
void main() {
fragOut0 = SMAANeighborhoodBlendingPS(fragTexCoord, fragOffset, colorTex, blendTex);
@@ -1,7 +1,13 @@
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
vec2 smaa_rt_metrics;
};
#else
layout (std140) uniform genericData {
vec2 smaa_rt_metrics;
};
#endif
#define SMAA_RT_METRICS vec4(1.0 / smaa_rt_metrics, smaa_rt_metrics)
@@ -10,6 +16,17 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
#ifdef VULKAN
layout(location = 0) out vec2 fragTexCoord;
layout(location = 1) out vec4 fragOffset;
void main() {
vec2 uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
fragTexCoord = uv;
gl_Position = vec4(uv * 2.0 - 1.0, 0.0, 1.0);
SMAANeighborhoodBlendingVS(uv, fragOffset);
}
#else
in vec4 vertPosition;
in vec2 vertTexCoord;
@@ -21,3 +38,4 @@ void main() {
fragTexCoord = vertTexCoord;
SMAANeighborhoodBlendingVS(vertTexCoord, fragOffset);
}
#endif
@@ -0,0 +1,14 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
void main()
{
fragOut0 = texture(tex, fragTexCoord.xy);
}
+33 -1
View File
@@ -1,11 +1,22 @@
#include "gamma.sdr"
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout(std140)
#endif
uniform genericData {
float exposure;
int tonemapper;
float x0; //from here on these are for the PPC tonemappers
@@ -19,6 +30,9 @@ layout (std140) uniform genericData {
float sh_lnA;
float sh_offsetX;
float sh_offsetY;
float hdr_paperwhite_nits;
float hdr_peak_nits;
};
//Tonemapping options, aside from the previous standard UC2, pulled from wookiejedi and qazwsxal's
//work on testing them for FSO, which itself was based on these references:
@@ -110,6 +124,20 @@ vec3 linearToneMapping(vec3 hdr_color)
return hdr_color;
}
// Display-referred, paper-white-relative. Unlike every other tonemapper here,
// this one does not compress to [0,1] -- it keeps linear values with
// head-room above paper white (up to peak_nits/paperwhite_nits), stored in
// the shared extended-sRGB encoding that the HDR10 output-encode pass
// (gamma-correct-f.sdr's HDR10_OUTPUT variant) later expects as its input.
// Only ever selected via TonemapperAlgorithm::HdrScene, which
// current_tonemapper() forces exclusively whenever HDR10 output is active.
vec3 hdrSceneToneMapping(vec3 hdr_color)
{
float headroom = max(hdr_peak_nits / max(hdr_paperwhite_nits, 1.0), 1.0);
hdr_color = min(hdr_color, vec3(headroom));
return hdr_color;
}
float toe( float x ) {
return exp(toe_lnA + toe_B * log(x));
}
@@ -175,6 +203,7 @@ vec3 PPC( vec3 color ) {
void main()
{
vec4 color = texture(tex, fragTexCoord.xy);
color *= exposure;
switch(tonemapper){
@@ -205,6 +234,9 @@ void main()
case 8:
color.rgb = PPC_RGB(color.rgb);
break;
case 9:
color.rgb = hdrSceneToneMapping(color.rgb);
break;
}
#ifdef LINEAR_OUT
+17 -1
View File
@@ -1,9 +1,25 @@
#ifdef VULKAN
layout(location = 0) in vec4 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray textures[16];
#define ytex textures[0]
#define utex textures[1]
#define vtex textures[2]
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2DArray ytex;
uniform sampler2DArray utex;
uniform sampler2DArray vtex;
layout (std140) uniform movieData {
#endif
#ifdef VULKAN
layout(std140, set = 2, binding = 4)
#else
layout(std140)
#endif
uniform movieData {
float alpha;
float pad[3];
};
+12 -1
View File
@@ -1,8 +1,19 @@
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
layout(location = 2) in vec4 vertTexCoord;
layout(location = 0) out vec4 fragTexCoord;
#else
in vec4 vertPosition;
in vec4 vertTexCoord;
out vec4 fragTexCoord;
#endif
layout (std140) uniform matrixData {
#ifdef VULKAN
layout(std140, set = 2, binding = 1)
#else
layout(std140)
#endif
uniform matrixData {
mat4 modelViewMatrix;
mat4 projMatrix;
};
+34 -7
View File
@@ -1,17 +1,31 @@
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex2D[16];
#define composite tex2D[0]
#define emissive tex2D[1]
layout(set = 1, binding = 4) uniform sampler2D depth_tex;
layout(set = 1, binding = 5) uniform sampler3D volume_tex;
layout(set = 1, binding = 6) uniform sampler3D noise_volume_tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
layout (binding = 0) uniform sampler2D composite;
layout (binding = 1) uniform sampler2D emissive;
layout (binding = 2) uniform sampler2D depth;
layout (binding = 2) uniform sampler2D depth_tex;
layout (binding = 3) uniform sampler3D volume_tex;
#ifdef NOISE
layout (binding = 4) uniform sampler3D noise_volume_tex;
#endif
#endif
layout (std140) uniform genericData {
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
layout (std140)
#endif
uniform genericData {
mat4 p_inv;
mat4 v_inv;
vec3 camera;
@@ -56,7 +70,13 @@ part of the volumetrics is adapted from Andrew Schneider's 2015 SIGGRAPH talk "T
void main()
{
#ifdef VULKAN
// Vulkan's clip space has an inverted Y relative to OpenGL, so flip the
// reconstructed NDC Y when building the eye-space ray direction.
vec4 eyeDirection = p_inv * vec4(fragTexCoord.x * 2.0 - 1.0, -fragTexCoord.y * 2.0 + 1.0, -1, 1);
#else
vec4 eyeDirection = p_inv * vec4(fragTexCoord.xy * 2.0 - 1.0, -1, 1);
#endif
eyeDirection.w = 0;
vec3 rayDirection = normalize((v_inv * eyeDirection).xyz);
@@ -73,12 +93,19 @@ void main()
vec2 fragcoordAngle = (fragTexCoord.xy - 0.5) * fov;
fragcoordAngle.x *= aspect;
float depth = (2.0 * zNear * zFar / (zFar + zNear - (2 * texture(depth, fragTexCoord.xy).x - 1) * (zFar - zNear))) * sqrt(1.0 + tan(fragcoordAngle.x) * tan(fragcoordAngle.x) + tan(fragcoordAngle.y) * tan(fragcoordAngle.y));
float rawDepth = texture(depth_tex, fragTexCoord.xy).x;
#ifdef VULKAN
// Vulkan depth range is [0,1]
float fragDepth = (zNear * zFar / (zFar - rawDepth * (zFar - zNear))) * sqrt(1.0 + tan(fragcoordAngle.x) * tan(fragcoordAngle.x) + tan(fragcoordAngle.y) * tan(fragcoordAngle.y));
#else
// OpenGL depth range is [-1,1]
float fragDepth = (2.0 * zNear * zFar / (zFar + zNear - (2.0 * rawDepth - 1.0) * (zFar - zNear))) * sqrt(1.0 + tan(fragcoordAngle.x) * tan(fragcoordAngle.x) + tan(fragcoordAngle.y) * tan(fragcoordAngle.y));
#endif
//The t at which the ray enters the nebula cube
float maxtMin = max(0,max(tMin.x, max(tMin.y, tMin.z)));
//The t at which the ray leaves the nebula cube
float mintMax = min(depth, min(tMax.x, min(tMax.y, tMax.z)));
float mintMax = min(fragDepth, min(tMax.x, min(tMax.y, tMax.z)));
//The cumulative one minus alpha value of the nebula. Corresponds to the multiplier of the current step when additively drawing to a fragment for which the steps closer to the camera are already drawn.
float cumOMAlpha = 1;
@@ -151,7 +178,7 @@ void main()
//Emissive
cumnebdist += stepcolor_alpha * stepsize_current;
vec3 emissive_lod = textureLod(emissive, fragTexCoord.xy, clamp(cumnebdist * emissiveSpreadFactor, 0, float(textureQueryLevels(emissive) - 1))).rgb;
vec3 stepcolor_emissive = clamp(emissive_lod.rgb * pow(alphalim, 1.0 / (opacitydistance / ((depth - stept) * emissiveFalloff + 0.01))) * emissiveIntensity, 0, 1);
vec3 stepcolor_emissive = clamp(emissive_lod.rgb * pow(alphalim, 1.0 / (opacitydistance / ((fragDepth - stept) * emissiveFalloff + 0.01))) * emissiveIntensity, 0, 1);
//Step finish
vec3 stepcolor = clamp(stepcolor_diffuse + stepcolor_emissive, 0, 1);
+12 -2
View File
@@ -23,13 +23,23 @@ public:
}
protected:
bool LoadExternal( const char* externlib )
bool LoadExternal( const char* externlib, const char* basePath = nullptr )
{
if ( !externlib )
return FALSE;
m_library = SDL_LoadObject(externlib);
// check full path as a fallback for our own libraries
// NOTE: basePath is assumed to have a trailing slash!
if ( !m_library && basePath )
{
SCP_string fullpath = basePath;
fullpath += externlib;
m_library = SDL_LoadObject(fullpath.c_str());
}
#ifndef NDEBUG
if (m_library == NULL)
{
@@ -113,4 +123,4 @@ typedef int (SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version*);
v->major = Major; v->minor = Minor; v->patch = Patch;\
return 0; }
#endif /* EXTERNALCODE_H_INCLUDED_ */
#endif /* EXTERNALCODE_H_INCLUDED_ */
+5
View File
@@ -597,6 +597,11 @@ inline void* memset_if_trivial_else_error(ImDrawListSplitter* memset_data, int c
return ptr_memcpy(memcpy_dest, memcpy_src, count);
}
inline void *memcpy_if_trivial_else_error(void *memcpy_dest, const void *memcpy_src, size_t count)
{
return ptr_memcpy(memcpy_dest, memcpy_src, count);
}
// MEMMOVE!
const auto ptr_memmove = std::memmove;
#define memmove memmove_if_trivial_else_error
+1 -1
View File
@@ -30,7 +30,7 @@
#ifdef NO_RESTRICT_USE
# define RESTRICT
#else
# define RESTRICT restrict
# define RESTRICT __restrict
#endif
#define ASSUME(x)
+1 -1
View File
@@ -31,7 +31,7 @@
#ifdef NO_RESTRICT_USE
# define RESTRICT
#else
# define RESTRICT restrict
# define RESTRICT __restrict
#endif
#define ASSUME(x)
+1 -1
View File
@@ -32,7 +32,7 @@
#ifdef NO_RESTRICT_USE
# define RESTRICT
#else
# define RESTRICT restrict
# define RESTRICT __restrict__
#endif
#define ASSUME(x)
+5 -2
View File
@@ -201,15 +201,18 @@ SCP_string get_version_string()
// Lets get some more info in here
switch (gr_screen.mode) {
case GR_OPENGL:
case GraphicsAPI::OpenGL:
str += " OpenGL";
#ifdef USE_OPENGL_ES
str += " ES";
#endif
break;
case GR_VULKAN:
case GraphicsAPI::Vulkan:
str += " Vulkan";
break;
default:
str += " Unknown???";
break;
}
return str;
+18 -1
View File
@@ -2,7 +2,24 @@
#include "globalincs/vmallocator.h"
std::locale SCP_default_locale("");
// std::locale("") pulls in the process's environment locale categories (LC_CTYPE,
// LC_MONETARY, LC_PAPER, etc.), any one of which can name a locale that isn't actually
// installed on the host (e.g. mismatched regional-format overrides). That throws
// std::runtime_error, and since this runs during static initialization -- before
// main(), before any of our own exception handling exists -- an uncaught throw here
// aborts the whole process before the game even starts. SCP_default_locale is only
// ever used for plain ASCII case-folding (SCP_toupper/tolower/isdigit), so falling
// back to the classic "C" locale is a safe, silent degradation.
static std::locale make_scp_default_locale()
{
try {
return std::locale("");
} catch (const std::runtime_error&) {
return std::locale::classic();
}
}
std::locale SCP_default_locale = make_scp_default_locale();
void SCP_tolower(SCP_string &str)
{
+141 -48
View File
@@ -80,9 +80,11 @@ gr_capability_def gr_capabilities[] = {
GR_CAPABILITY_ENTRY(SEPARATE_BLEND_FUNCTIONS),
GR_CAPABILITY_ENTRY(PERSISTENT_BUFFER_MAPPING),
gr_capability_def {gr_capability::CAPABILITY_BPTC, "BPTC Texture Compression"}, //This one had a different parse string already!
gr_capability_def {gr_capability::CAPABILITY_S3TC, "S3TC Texture Compression"},
GR_CAPABILITY_ENTRY(LARGE_SHADER),
GR_CAPABILITY_ENTRY(INSTANCED_RENDERING),
GR_CAPABILITY_ENTRY(FAST_SHADOWS),
GR_CAPABILITY_ENTRY(RAYTRACED_SHADOWS),
};
const size_t gr_capabilities_num = sizeof(gr_capabilities) / sizeof(gr_capabilities[0]);
@@ -753,6 +755,75 @@ void removeVSyncOption()
options::OptionsManager::instance()->removeOption(VSyncOption);
}
bool Gr_enable_hdr = false;
bool Gr_hdr_output_active = false;
static void parse_hdr_func()
{
bool value;
stuff_boolean(&value);
Gr_enable_hdr = value;
}
// coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton
static auto HDROption __UNUSED = options::OptionBuilder<bool>("Graphics.HDR",
std::pair<const char*, int>{"HDR Output", -1},
std::pair<const char*, int>{"Enables HDR10 (PQ / BT.2020) output on supported displays. Vulkan renderer only. Requires a restart to take effect.", -1})
.category(std::make_pair("Graphics", 1825))
.level(options::ExpertLevel::Advanced)
.default_func([]() { return Gr_enable_hdr; })
.bind_to_once(&Gr_enable_hdr)
.importance(68)
.parser(parse_hdr_func)
.finish();
float Gr_hdr_paperwhite_nits = 200.0f;
static void parse_hdr_paperwhite_func()
{
float value;
stuff_float(&value);
Gr_hdr_paperwhite_nits = value;
}
// coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton
static auto HDRPaperWhiteOption __UNUSED = options::OptionBuilder<float>("Graphics.HDRPaperWhite",
std::pair<const char*, int>{"HDR Paper White", -1},
std::pair<const char*, int>{"Reference white luminance in nits for UI and SDR-referenced content when HDR output is active.", -1})
.category(std::make_pair("Graphics", 1825))
.level(options::ExpertLevel::Advanced)
.default_func([]() { return Gr_hdr_paperwhite_nits; })
.range(80.0f, 480.0f)
.bind_to(&Gr_hdr_paperwhite_nits)
.importance(67)
.parser(parse_hdr_paperwhite_func)
.finish();
float Gr_hdr_peak_nits = 1000.0f;
static void parse_hdr_peak_func()
{
float value;
stuff_float(&value);
Gr_hdr_peak_nits = value;
}
// coverity[GLOBAL_INIT_ORDER] -- safe; OptionBuilder::finish() uses Meyers singleton
static auto HDRPeakOption __UNUSED = options::OptionBuilder<float>("Graphics.HDRPeakLuminance",
std::pair<const char*, int>{"HDR Peak Luminance", -1},
std::pair<const char*, int>{"Display peak luminance in nits used for HDR tone curve clamping and HDR10 metadata.", -1})
.category(std::make_pair("Graphics", 1825))
.level(options::ExpertLevel::Advanced)
.default_func([]() { return Gr_hdr_peak_nits; })
.range(400.0f, 4000.0f)
.bind_to(&Gr_hdr_peak_nits)
.importance(66)
.parser(parse_hdr_peak_func)
.finish();
static std::unique_ptr<graphics::util::UniformBufferManager> UniformBufferManager;
// Forward definitions
@@ -1316,28 +1387,30 @@ void gr_close()
graphics::paths::PathRenderer::shutdown();
// Free bitmaps before destroying the graphics backend, since
// gf_bm_free_data needs the backend (texture manager, GL context, etc.)
bm_close();
switch (gr_screen.mode) {
case GR_OPENGL:
case GraphicsAPI::OpenGL:
#ifdef WITH_OPENGL
gr_opengl_cleanup(true);
#endif
break;
case GR_VULKAN:
case GraphicsAPI::Vulkan:
#ifdef WITH_VULKAN
graphics::vulkan::cleanup();
#endif
break;
case GR_STUB:
case GraphicsAPI::Stub:
break;
default:
Int3(); // Invalid graphics mode
}
bm_close();
Gr_inited = 0;
}
@@ -1527,33 +1600,33 @@ static void init_colors()
Gr_ta_alpha.scale = 17;
}
static void gr_init_function_pointers(int mode) {
static void gr_init_function_pointers(GraphicsAPI mode) {
gr_screen = {};
switch (mode) {
case GR_OPENGL:
case GraphicsAPI::OpenGL:
#ifdef WITH_OPENGL
gr_opengl_init_function_pointers();
#else
Error(LOCATION, "OpenGL renderer was requested but that was not compiled into this build.");
#endif
break;
case GR_VULKAN:
case GraphicsAPI::Vulkan:
#ifdef WITH_VULKAN
graphics::vulkan::initialize_function_pointers();
#else
Error(LOCATION, "Vulkan renderer was requested but that was not compiled into this build.");
#endif
break;
case GR_STUB:
case GraphicsAPI::Stub:
gr_stub_init_function_pointers();
break;
default:
UNREACHABLE("Invalid graphics mode %d requested", mode); // Invalid graphics mode
UNREACHABLE("Invalid graphics mode requested"); // Invalid graphics mode
}
}
static bool gr_init_sub(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int mode, int width, int height,
static bool gr_init_sub(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, GraphicsAPI mode, int width, int height,
int depth, float center_aspect_ratio)
{
int res = GR_1024;
@@ -1587,7 +1660,6 @@ static bool gr_init_sub(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, i
if (Fred_running) {
gr_screen.custom_size = false;
res = GR_640;
mode = GR_OPENGL;
}
Save_custom_screen_size = gr_screen.custom_size;
@@ -1655,7 +1727,7 @@ static bool gr_init_sub(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, i
init_colors();
switch (mode) {
case GR_OPENGL:
case GraphicsAPI::OpenGL:
#ifdef WITH_OPENGL
rc = gr_opengl_init(std::move(graphicsOps));
#else
@@ -1663,7 +1735,7 @@ static bool gr_init_sub(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, i
rc = false;
#endif
break;
case GR_VULKAN:
case GraphicsAPI::Vulkan:
#ifdef WITH_VULKAN
rc = graphics::vulkan::initialize(std::move(graphicsOps));
#else
@@ -1671,7 +1743,7 @@ static bool gr_init_sub(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, i
rc = false;
#endif
break;
case GR_STUB:
case GraphicsAPI::Stub:
SCP_UNUSED(graphicsOps);
rc = gr_stub_init();
break;
@@ -1727,21 +1799,22 @@ SCP_string gr_capability_to_human_readable_string(gr_capability capability)
return "Invalid Capability";
}
bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode, int d_width, int d_height, int d_depth)
bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, GraphicsAPI d_mode, int d_width, int d_height, int d_depth)
{
int width = 1024, height = 768, depth = 32, mode = GR_OPENGL;
int width = 1024, height = 768, depth = 32;
GraphicsAPI mode = GraphicsAPI::OpenGL;
float center_aspect_ratio = -1.0f;
const char *ptr = NULL;
// If already inited, shutdown the previous graphics
if (Gr_inited) {
switch (gr_screen.mode) {
case GR_OPENGL:
case GraphicsAPI::OpenGL:
#ifdef WITH_OPENGL
gr_opengl_cleanup(false);
#endif
break;
case GR_STUB:
case GraphicsAPI::Stub:
break;
default:
@@ -1751,7 +1824,7 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
if (Using_in_game_options) {
if (Cmdline_enable_vr) {
// in VR mode, so set resolution using VR values
// in VR mode, so set resolution using VR values
// and hide/disable the default resolution option
auto res = ResolutionVROption->getValue();
width = res.width;
@@ -1765,6 +1838,7 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
height = res.height;
removeResolutionVROption();
}
//TODO set d_mode from Ingame Options if available here
} else if ( !Is_standalone ) {
// We cannot continue without this, quit, but try to help the user out first
ptr = os_config_read_string(nullptr, NOX("VideocardFs2open"), nullptr);
@@ -1824,26 +1898,31 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
SCP_string videoApi(ptr, ptr + 4);
if (videoApi == "OGL ") {
d_mode = GR_OPENGL; // GR_OPENGL;
d_mode = GraphicsAPI::OpenGL; // GR_OPENGL;
} else if (videoApi == "VK ") {
d_mode = GR_VULKAN;
d_mode = GraphicsAPI::Vulkan;
} else {
ReleaseWarning(LOCATION, "Unknown video API '%s'", videoApi.c_str());
}
}
if (Cmdline_res != nullptr) {
int tmp_width = 0;
int tmp_height = 0;
if ( sscanf(Cmdline_res, "%dx%d", &tmp_width, &tmp_height) == 2 ) {
width = tmp_width;
height = tmp_height;
}
}
Gr_enable_soft_particles = Cmdline_softparticles != 0;
}
//Commandline ALWAYS wins for Graphics API and resolution
if (Cmdline_graphics_api != GraphicsAPI::Default)
d_mode = Cmdline_graphics_api;
if (Cmdline_res != nullptr) {
int tmp_width = 0;
int tmp_height = 0;
if ( sscanf(Cmdline_res, "%dx%d", &tmp_width, &tmp_height) == 2 ) {
width = tmp_width;
height = tmp_height;
}
}
if (Cmdline_center_res != NULL) {
int tmp_center_width = 0;
int tmp_center_height = 0;
@@ -1871,9 +1950,9 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
Cmdline_window_res.emplace(static_cast<uint16_t>(width), static_cast<uint16_t>(height));
}
if (d_mode == GR_DEFAULT) {
// OpenGL should be default
mode = GR_OPENGL;
if (d_mode == GraphicsAPI::Default) {
// OpenGL should be default.
mode = GraphicsAPI::OpenGL;
} else {
mode = d_mode;
}
@@ -1890,13 +1969,21 @@ bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode,
// if we are in standalone mode then just use special defaults
if (Is_standalone) {
mode = GR_STUB;
mode = GraphicsAPI::Stub;
width = 640;
height = 480;
depth = 16;
center_aspect_ratio = -1.0f;
}
// FRED doesn't support Vulkan yet (see qtfred/README.md for what's needed to change that), so it always
// falls back to OpenGL regardless of what was requested. This must happen before gr_init_function_pointers()
// below, since that's what binds gr_screen's gf_* dispatch table to the chosen API; doing the override any
// later (e.g. in gr_init_sub()) would leave the dispatch table pointing at the wrong backend.
if (Fred_running) {
mode = GraphicsAPI::OpenGL;
}
gr_init_function_pointers(mode);
if (gr_get_resolution_class(width, height) != GR_640) {
@@ -2182,7 +2269,7 @@ void gr_bitmap(int _x, int _y, int resize_mode, bool mirror, float scale_factor)
float x, y, w, h;
vertex verts[4];
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -2903,7 +2990,7 @@ void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float a
static void output_uniform_debug_data()
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -2935,6 +3022,16 @@ void gr_flip(bool execute_scripting)
model_process_cached_ui_render_instances();
if (Cmdline_graphics_debug_output) {
output_uniform_debug_data();
}
// IMPORTANT: No rendering may happen after this point until gf_flip()/gr_setup_frame().
// gr_reset_immediate_buffer() resets the write offset to 0, so any subsequent immediate
// buffer write would overwrite vertex data that already-recorded draw commands reference.
// In Vulkan (deferred submission), the GPU reads the final buffer state at submit time,
// so overwrites here silently corrupt earlier draws. OpenGL's immediate execution hides
// this, but it is still logically wrong for any deferred-submission backend.
gr_reset_immediate_buffer();
// Do per frame operations on the matrix state
@@ -2944,10 +3041,6 @@ void gr_flip(bool execute_scripting)
mouse_reset_deltas();
if (Cmdline_graphics_debug_output) {
output_uniform_debug_data();
}
// Use this opportunity for retiring the uniform buffers
uniform_buffer_managers_retire_buffers();
@@ -2990,7 +3083,7 @@ void gr_print_timestamp(int x, int y, fix timestamp, int resize_mode)
void gr_uniform_buffer_managers_init()
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -2999,7 +3092,7 @@ void gr_uniform_buffer_managers_init()
static void uniform_buffer_managers_deinit()
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -3008,7 +3101,7 @@ static void uniform_buffer_managers_deinit()
static void uniform_buffer_managers_retire_buffers()
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -3149,7 +3242,7 @@ size_t vertex_layout::hash() const {
static std::unique_ptr<graphics::util::GPUMemoryHeap> gpu_heaps [static_cast<size_t>(GpuHeap::NUM_VALUES)];
static void gpu_heap_init() {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -3192,7 +3285,7 @@ void gr_heap_deallocate(GpuHeap heap_type, size_t data_offset)
void gr_set_gamma(float gamma)
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
+83 -12
View File
@@ -41,6 +41,9 @@ extern float Min_draw_distance;
extern float Max_draw_distance;
extern int Gr_inited;
// # Software Re-added by Kazan --- THIS HAS TO STAY -- It is used by standalone!
enum class GraphicsAPI : uint8_t { Default, Stub, OpenGL, Vulkan };
// z-buffering stuff
extern int gr_zbuffering, gr_zbuffering_mode;
extern int gr_global_zbuffering;
@@ -72,6 +75,16 @@ extern bool Gr_post_processing_enabled;
extern bool Gr_enable_vsync;
// HDR10 (PQ/ST.2084 + BT.2020) output. Currently only honored by the Vulkan renderer.
extern bool Gr_enable_hdr;
// True once the renderer has actually negotiated an HDR10 swap chain (read-only,
// set by the active renderer). Distinct from Gr_enable_hdr, which is the request.
extern bool Gr_hdr_output_active;
// Reference white luminance in nits (the brightness of SDR "paper white" / UI when HDR is active).
extern float Gr_hdr_paperwhite_nits;
// Display peak luminance in nits used for tone curve clamping and HDR10 metadata.
extern float Gr_hdr_peak_nits;
extern bool Deferred_lighting;
extern bool High_dynamic_range;
@@ -209,13 +222,14 @@ enum shader_type {
SDR_TYPE_POST_PROCESS_SMAA_EDGE,
SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT,
SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING,
SDR_TYPE_POST_PROCESS_SMAA_RESOLVE,
SDR_TYPE_ENVMAP_SPHERE_WARP,
SDR_TYPE_IRRADIANCE_MAP_GEN,
SDR_TYPE_SHADOW_MAP_GEN,
SDR_TYPE_GAMMA_BLIT,
NUM_SHADER_TYPES
@@ -240,10 +254,16 @@ enum shader_type {
#define SDR_FLAG_VOLUMETRICS_NOISE (1<<1)
#define SDR_FLAG_COPY_FROM_ARRAY (1 << 0)
#define SDR_FLAG_COPY_CLAMP01 (1 << 1)
#define SDR_FLAG_TONEMAPPING_LINEAR_OUT (1 << 0)
#define SDR_FLAG_GAMMA_HDR10 (1 << 0)
#define SDR_FLAG_ENV_MAP (1 << 0)
#define SDR_FLAG_DEFERRED_RT_SHADOWS (1 << 1)
#define SDR_FLAG_SHADOW_FALLBACK (1 << 0)
#define SDR_FLAG_SHADOW_FALLBACK (1 << 0)
@@ -270,7 +290,6 @@ struct vertex_format_data
POSITION4,
POSITION3,
POSITION2,
SCREEN_POS,
COLOR3,
COLOR4,
COLOR4F,
@@ -346,9 +365,12 @@ enum class gr_capability {
CAPABILITY_SEPARATE_BLEND_FUNCTIONS,
CAPABILITY_PERSISTENT_BUFFER_MAPPING,
CAPABILITY_BPTC,
CAPABILITY_S3TC,
CAPABILITY_LARGE_SHADER,
CAPABILITY_INSTANCED_RENDERING,
CAPABILITY_FAST_SHADOWS,
CAPABILITY_QUERIES_REUSABLE,
CAPABILITY_RAYTRACED_SHADOWS
};
struct gr_capability_def {
@@ -680,7 +702,7 @@ typedef struct screen {
int save_center_w = 0, save_center_h = 0; // Width and height of center monitor
int save_center_offset_x = 0, save_center_offset_y = 0;
int res = 0; // GR_640 or GR_1024
int mode = 0; // What mode gr_init was called with.
GraphicsAPI mode = GraphicsAPI::Default; // What mode gr_init was called with.
float aspect = 0.0f, clip_aspect = 0.0f; // Aspect ratio = 0, aspect of clip_width/clip_height
int rowsize = 0; // What you need to add to go to next row (includes bytes_per_pixel)
int bits_per_pixel = 0; // How many bits per pixel it is. (7,8,15,16,24,32)
@@ -798,9 +820,22 @@ typedef struct screen {
std::function<void(int)> gf_set_texture_addressing;
std::function<gr_buffer_handle(BufferType type, BufferUsageHint usage)> gf_create_buffer;
std::function<gr_buffer_handle(BufferType type, BufferUsageHint usage, bool rt_capable)> gf_create_buffer;
std::function<void(gr_buffer_handle handle)> gf_delete_buffer;
// Optional lifecycle hooks for backends that maintain auxiliary per-model GPU
// state (e.g. Vulkan's raytraced-shadow BLAS cache). Default to no-ops so
// backends that don't need them (OpenGL, stub) don't have to assign anything.
std::function<void(int pm_id)> gf_model_loaded = [](int) {};
std::function<void(int pm_id)> gf_model_unloaded = [](int) {};
// Rebuilds the raytraced-shadow top-level acceleration structure for the
// current frame from the live shadow-casting object set. Called once per
// frame from shadows_render_all(), alongside the cascaded shadow map pass --
// the shading pass picks between the two per shadows_use_raytracing().
// No-op default.
std::function<void()> gf_build_shadow_tlas = []() {};
std::function<void(gr_buffer_handle handle, size_t size, const void* data)> gf_update_buffer_data;
std::function<void(gr_buffer_handle handle, size_t offset, size_t size, const void* data)>
gf_update_buffer_data_offset;
@@ -948,6 +983,10 @@ typedef struct screen {
std::function<void(bool set_override)> gf_override_fog;
// ImGui backend integration
std::function<void()> gf_imgui_new_frame;
std::function<void()> gf_imgui_render_draw_data;
//OpenXR functions
std::function<SCP_vector<const char*>()> gf_openxr_get_extensions;
std::function<bool()> gf_openxr_test_capabilities;
@@ -1000,11 +1039,7 @@ bool gr_lua_context_active();
//--------------------------------------
// Call this at application startup
// # Software Re-added by Kazan --- THIS HAS TO STAY -- It is used by standalone!
#define GR_DEFAULT (-1) // set to use default settings
#define GR_STUB (100)
#define GR_OPENGL (104) // Use OpenGl hardware renderer
#define GR_VULKAN (105) // Use Vulkan hardware renderer
#define GR_DEFAULT (-1)
// resolution constants - always keep resolutions in ascending order and starting from 0
#define GR_NUM_RESOLUTIONS 2
@@ -1016,7 +1051,7 @@ bool gr_lua_context_active();
extern const char *Resolution_prefixes[GR_NUM_RESOLUTIONS];
extern bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, int d_mode = GR_DEFAULT,
extern bool gr_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps, GraphicsAPI d_mode = GraphicsAPI::Default,
int d_width = GR_DEFAULT, int d_height = GR_DEFAULT, int d_depth = GR_DEFAULT);
extern void gr_screen_resize(int width, int height);
@@ -1152,12 +1187,45 @@ inline int gr_bm_set_render_target(int n, int face = -1)
#define gr_set_texture_addressing GR_CALL(gr_screen.gf_set_texture_addressing)
inline gr_buffer_handle gr_create_buffer(BufferType type, BufferUsageHint usage)
/**
* @brief Create a GPU buffer
* @param type The buffer type (Vertex, Index, Uniform)
* @param usage Usage hint for optimization
* @param rt_capable If true (Vulkan only), the buffer is created with the extra
* usage flags (VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT and
* VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR)
* needed to use it as acceleration structure geometry input. Ignored by
* backends without raytraced shadow support.
*/
inline gr_buffer_handle gr_create_buffer(BufferType type, BufferUsageHint usage, bool rt_capable = false)
{
return gr_screen.gf_create_buffer(type, usage);
return gr_screen.gf_create_buffer(type, usage, rt_capable);
}
#define gr_delete_buffer GR_CALL(gr_screen.gf_delete_buffer)
/**
* @brief Notify the graphics backend that a model finished loading (its GPU
* vertex/index buffers are already uploaded at this point). No-op on backends
* that don't need per-model GPU state.
*/
inline void gr_model_loaded(int pm_id)
{
gr_screen.gf_model_loaded(pm_id);
}
/**
* @brief Notify the graphics backend that a model is about to be freed, so it
* can release any auxiliary per-model GPU state before the model's own GPU
* buffers are destroyed.
*/
inline void gr_model_unloaded(int pm_id)
{
gr_screen.gf_model_unloaded(pm_id);
}
#define gr_build_shadow_tlas GR_CALL(gr_screen.gf_build_shadow_tlas)
#define gr_update_buffer_data GR_CALL(gr_screen.gf_update_buffer_data)
#define gr_update_buffer_data_offset GR_CALL(gr_screen.gf_update_buffer_data_offset)
inline void* gr_map_buffer(gr_buffer_handle handle)
@@ -1210,6 +1278,9 @@ inline void gr_post_process_restore_zbuffer()
#define gr_override_fog GR_CALL(gr_screen.gf_override_fog)
#define gr_imgui_new_frame GR_CALL(gr_screen.gf_imgui_new_frame)
#define gr_imgui_render_draw_data GR_CALL(gr_screen.gf_imgui_render_draw_data)
inline void gr_render_primitives(material* material_info,
primitive_type prim_type,
vertex_layout* layout,
+1 -1
View File
@@ -28,7 +28,7 @@ uint gr_stub_lock()
return 1;
}
gr_buffer_handle gr_stub_create_buffer(BufferType, BufferUsageHint)
gr_buffer_handle gr_stub_create_buffer(BufferType, BufferUsageHint, bool)
{
return {};
}
+7 -7
View File
@@ -201,7 +201,7 @@ static void pre_render_init_lights() {
}
void gr_set_light(light* fs_light) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -213,7 +213,7 @@ void gr_set_light(light* fs_light) {
}
void gr_set_center_alpha(int type) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -274,7 +274,7 @@ void gr_set_center_alpha(int type) {
}
void gr_reset_lighting() {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -287,7 +287,7 @@ void gr_light_shutdown() {
}
void gr_light_init() {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -296,7 +296,7 @@ void gr_light_init() {
}
void gr_set_lighting() {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -329,7 +329,7 @@ void gr_set_lighting() {
}
void gr_set_ambient_light(int red, int green, int blue) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -357,7 +357,7 @@ void gr_get_ambient_light(vec3d* light_vector) {
}
void gr_lighting_fill_uniforms(void* data_out, size_t buffer_size) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
+8
View File
@@ -2,6 +2,7 @@
#include "graphics/grinternal.h"
#include "graphics/2d.h"
#include "graphics/material.h"
#include "graphics/shadows.h"
#include "globalincs/systemvars.h"
#include "cmdline/cmdline.h"
@@ -742,6 +743,13 @@ uint model_material::get_shader_flags() const
Shader_flags |= MODEL_SDR_FLAG_THICK_OUTLINES;
}
// Must stay a genuine compile-time flag (like SHADOW_MAP/THICK_OUTLINES above),
// not one gated only via the runtime `flags` uniform below -- see the comment
// on MODEL_SDR_FLAG_RT_SHADOWS in model_shader_flags.h.
if (is_shadow_receiving() && shadows_use_raytracing()) {
Shader_flags |= MODEL_SDR_FLAG_RT_SHADOWS;
}
if (!gr_is_capable(gr_capability::CAPABILITY_LARGE_SHADER)) {
Shader_flags |= get_shader_runtime_flags();
}
+28 -5
View File
@@ -2,6 +2,7 @@
#include "matrix.h"
#include "graphics/util/UniformBuffer.h"
#include "cmdline/cmdline.h"
#include <graphics/util/uniform_structs.h>
@@ -56,9 +57,18 @@ static void create_perspective_projection_matrix(matrix4 *out, float left, float
out->a1d[5] = 2.0f * near_dist / (top - bottom);
out->a1d[8] = (right + left) / (right - left);
out->a1d[9] = (top + bottom) / (top - bottom);
out->a1d[10] = -(far_dist + near_dist) / (far_dist - near_dist);
out->a1d[11] = -1.0f;
out->a1d[14] = -2.0f * far_dist * near_dist / (far_dist - near_dist);
if (gr_screen.mode == GraphicsAPI::Vulkan) {
// Vulkan NDC Z range is [0, 1] (OpenGL is [-1, 1])
// Y-flip is handled by negative viewport height (VK_KHR_maintenance1)
out->a1d[10] = -far_dist / (far_dist - near_dist);
out->a1d[14] = -far_dist * near_dist / (far_dist - near_dist);
} else {
// OpenGL NDC Z range is [-1, 1]
out->a1d[10] = -(far_dist + near_dist) / (far_dist - near_dist);
out->a1d[14] = -2.0f * far_dist * near_dist / (far_dist - near_dist);
}
}
static void create_orthographic_projection_matrix(matrix4* out, float left, float right, float bottom, float top, float near_dist, float far_dist)
@@ -67,11 +77,20 @@ static void create_orthographic_projection_matrix(matrix4* out, float left, floa
out->a1d[0] = 2.0f / (right - left);
out->a1d[5] = 2.0f / (top - bottom);
out->a1d[10] = -2.0f / (far_dist - near_dist);
out->a1d[12] = -(right + left) / (right - left);
out->a1d[13] = -(top + bottom) / (top - bottom);
out->a1d[14] = -(far_dist + near_dist) / (far_dist - near_dist);
out->a1d[15] = 1.0f;
if (gr_screen.mode == GraphicsAPI::Vulkan) {
// Vulkan NDC Z range is [0, 1] (OpenGL is [-1, 1])
// Y-flip is handled by negative viewport height (VK_KHR_maintenance1)
out->a1d[10] = -1.0f / (far_dist - near_dist);
out->a1d[14] = -near_dist / (far_dist - near_dist);
} else {
// OpenGL NDC Z range is [-1, 1]
out->a1d[10] = -2.0f / (far_dist - near_dist);
out->a1d[14] = -(far_dist + near_dist) / (far_dist - near_dist);
}
}
void gr_start_instance_matrix(const vec3d *offset, const matrix *rotation)
@@ -286,7 +305,11 @@ void gr_end_2d_matrix()
Assert( htl_2d_matrix_depth == 1 );
// reset viewport to what it was originally set to by the proj matrix
gr_set_viewport(gr_screen.offset_x, (gr_screen.max_h - gr_screen.offset_y - gr_screen.clip_height), gr_screen.clip_width, gr_screen.clip_height);
if (gr_screen.rendering_to_texture != -1) {
gr_set_viewport(gr_screen.offset_x, gr_screen.offset_y, gr_screen.clip_width, gr_screen.clip_height);
} else {
gr_set_viewport(gr_screen.offset_x, (gr_screen.max_h - gr_screen.offset_y - gr_screen.clip_height), gr_screen.clip_width, gr_screen.clip_height);
}
gr_projection_matrix = gr_last_projection_matrix;
+7 -106
View File
@@ -4,6 +4,7 @@
#include <vector>
#include <glad/glad.h>
#include <KHR/khrplatform.h>
#include "graphics/util/pixel_swizzle.h"
/*
OpenGL ES 3.2 compatibility layer
---------------------------------
@@ -58,106 +59,6 @@
#define glGetDebugMessageLogARB glGetDebugMessageLog // ES 3.2
#define glDepthRange glDepthRangef // ES 3.0
/*
Pixel Conversion for unsupported uncompressed formats
*/
// BGRA 1_5_5_5_REV -> RGBA 5_5_5_1
static inline void convert_BGRA1555_REV_to_RGBA5551(const uint16_t* __restrict src, uint16_t* __restrict dst, size_t npx)
{
for (size_t i = 0; i < npx; i++) {
const uint16_t s = src[i];
uint16_t a = (s >> 15) & 0x1;
uint16_t r = (s >> 10) & 0x1F;
uint16_t g = (s >> 5) & 0x1F;
uint16_t b = (s >> 0) & 0x1F;
dst[i] = static_cast<uint16_t>((r << 11) | (g << 6) | (b << 1) | a);
}
}
// BGRA1555_REV -> RGBA8888
static inline void convert_BGRA1555_REV_to_RGBA8888(const uint16_t* __restrict src, uint8_t* __restrict dstRGBA8, size_t npx)
{
for (size_t i = 0; i < npx; ++i) {
uint16_t s = src[i];
uint8_t A = (s >> 15) ? 255 : 0;
uint8_t R5 = (s >> 10) & 0x1F;
uint8_t G5 = (s >> 5) & 0x1F;
uint8_t B5 = s & 0x1F;
// expand 5 to 8 bits
uint8_t R = (R5 << 3) | (R5 >> 2);
uint8_t G = (G5 << 3) | (G5 >> 2);
uint8_t B = (B5 << 3) | (B5 >> 2);
size_t o = 4 * i;
dstRGBA8[o + 0] = R;
dstRGBA8[o + 1] = G;
dstRGBA8[o + 2] = B;
dstRGBA8[o + 3] = A;
}
}
// BGR -> RGB
static inline void convert_BGR_to_RGB(const uint8_t* __restrict src, uint8_t* __restrict dst, size_t npx)
{
for (size_t i = 0, s = 0, t = 0; i < npx; ++i, s += 3, t += 3) {
uint8_t b = src[s + 0];
uint8_t g = src[s + 1];
uint8_t r = src[s + 2];
dst[t + 0] = r;
dst[t + 1] = g;
dst[t + 2] = b;
}
}
// BGRA 8888 -> RGBA 8888
static inline void convert_BGRA8888_to_RGBA8888(const uint8_t* __restrict src, uint8_t* __restrict dst, size_t npx)
{
for (size_t i = 0; i < npx; i++) {
const uint8_t b = src[4 * i + 0];
const uint8_t g = src[4 * i + 1];
const uint8_t r = src[4 * i + 2];
const uint8_t a = src[4 * i + 3];
dst[4 * i + 0] = r;
dst[4 * i + 1] = g;
dst[4 * i + 2] = b;
dst[4 * i + 3] = a;
}
}
// BGR 3B -> RGBA 4B
static inline void convert_BGR_to_RGBA(const uint8_t* __restrict src, uint8_t* __restrict dst, size_t npx)
{
for (size_t i = 0; i < npx; ++i) {
const size_t s = i * 3;
const size_t t = i * 4;
dst[t + 0] = src[s + 2]; // R <- B
dst[t + 1] = src[s + 1]; // G
dst[t + 2] = src[s + 0]; // B <- R
dst[t + 3] = 255; // A
}
}
// BGRA1555_REV -> RGB888
static inline void convert_BGRA1555_REV_to_RGB888(const uint16_t* __restrict src, uint8_t* __restrict dstRGB8, size_t npx)
{
for (size_t i = 0; i < npx; ++i) {
uint16_t s = src[i];
uint8_t R5 = (s >> 10) & 0x1F;
uint8_t G5 = (s >> 5) & 0x1F;
uint8_t B5 = s & 0x1F;
// expand 5 to 8 bits
uint8_t R = (R5 << 3) | (R5 >> 2);
uint8_t G = (G5 << 3) | (G5 >> 2);
uint8_t B = (B5 << 3) | (B5 >> 2);
size_t o = 3 * i;
dstRGB8[o + 0] = R;
dstRGB8[o + 1] = G;
dstRGB8[o + 2] = B;
}
}
/*
Intercept calls to do pixel conversion and format adjustments to something the driver can work with
Note: This is only needed for uncompressed texture formats
@@ -187,7 +88,7 @@ static inline void glTexSubImage3D(GLenum target, GLint level, GLint xoff, GLint
if (data != nullptr)
{
std::vector<uint8_t> scratch(npx * 4); // RGBA8888 = 4 BPP
convert_BGRA1555_REV_to_RGBA8888(reinterpret_cast<const uint16_t*>(data), scratch.data(), npx);
graphics::util::convert_BGRA1555_REV_to_RGBA8888(reinterpret_cast<const uint16_t*>(data), scratch.data(), npx);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glad_glTexSubImage3D(target, level, xoff, yoff, zoff, w, h, d, format, type, scratch.data());
return;
@@ -198,7 +99,7 @@ static inline void glTexSubImage3D(GLenum target, GLint level, GLint xoff, GLint
if (data != nullptr)
{
std::vector<uint8_t> scratch(npx * 3); // RGB888 = 3 BPP
convert_BGRA1555_REV_to_RGB888(reinterpret_cast<const uint16_t*>(data), scratch.data(), npx);
graphics::util::convert_BGRA1555_REV_to_RGB888(reinterpret_cast<const uint16_t*>(data), scratch.data(), npx);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glad_glTexSubImage3D(target, level, xoff, yoff, zoff, w, h, d, format, type, scratch.data());
return;
@@ -208,7 +109,7 @@ static inline void glTexSubImage3D(GLenum target, GLint level, GLint xoff, GLint
type = GL_UNSIGNED_SHORT_5_5_5_1;
if (data != nullptr) {
std::vector<uint8_t> scratch(npx * 2); // RGBA5551 = 2 BPP
convert_BGRA1555_REV_to_RGBA5551(reinterpret_cast<const uint16_t*>(data),
graphics::util::convert_BGRA1555_REV_to_RGBA5551(reinterpret_cast<const uint16_t*>(data),
reinterpret_cast<uint16_t*>(scratch.data()),
npx);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
@@ -226,7 +127,7 @@ static inline void glTexSubImage3D(GLenum target, GLint level, GLint xoff, GLint
format = GL_RGBA;
if (data != nullptr) {
std::vector<uint8_t> scratch(npx * 4); // RGBA8888 = 4 BPP
convert_BGR_to_RGBA(static_cast<const uint8_t*>(data), scratch.data(), npx);
graphics::util::convert_BGR_to_RGBA(static_cast<const uint8_t*>(data), scratch.data(), npx);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glad_glTexSubImage3D(target, level, xoff, yoff, zoff, w, h, d, format, type, scratch.data());
return;
@@ -237,7 +138,7 @@ static inline void glTexSubImage3D(GLenum target, GLint level, GLint xoff, GLint
format = GL_RGB;
if (data != nullptr) {
std::vector<uint8_t> scratch(npx * 3); // RGB888 = 3 BPP
convert_BGR_to_RGB(static_cast<const uint8_t*>(data), scratch.data(), npx);
graphics::util::convert_BGR_to_RGB(static_cast<const uint8_t*>(data), scratch.data(), npx);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glad_glTexSubImage3D(target, level, xoff, yoff, zoff, w, h, d, format, type, scratch.data());
return;
@@ -251,7 +152,7 @@ static inline void glTexSubImage3D(GLenum target, GLint level, GLint xoff, GLint
if (data != nullptr /* && !GLAD_GL_EXT_texture_format_BGRA8888*/) {
// Conversion forced on because the check either does not work or buggy impl on Mali
std::vector<uint8_t> scratch(npx * 4);
convert_BGRA8888_to_RGBA8888(reinterpret_cast<const uint8_t*>(data), scratch.data(), npx);
graphics::util::convert_BGRA8888_to_RGBA8888(reinterpret_cast<const uint8_t*>(data), scratch.data(), npx);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glad_glTexSubImage3D(target, level, xoff, yoff, zoff, w, h, d, GL_RGBA, type, scratch.data());
return;
+25 -3
View File
@@ -46,6 +46,8 @@
#include "es_compatibility.h"
#endif
#include "backends/imgui_impl_opengl3.h"
#include <glad/glad.h>
// minimum GL / GLES version we can reliably support is 3.2
@@ -148,8 +150,8 @@ void gr_opengl_flip()
GL_state.ColorMask(true, true, true, true);
GL_state.StencilTest(GL_FALSE);
opengl_set_generic_uniform_data<graphics::generic_data::gamma_blit_data>(
[gamma](graphics::generic_data::gamma_blit_data* data) {
opengl_set_generic_uniform_data<graphics::generic_data::gamma_encode_data>(
[gamma](graphics::generic_data::gamma_encode_data* data) {
data->gamma = gamma;
});
@@ -1040,6 +1042,16 @@ int opengl_init_display_device()
return 0;
}
static void gr_opengl_imgui_new_frame()
{
ImGui_ImplOpenGL3_NewFrame();
}
static void gr_opengl_imgui_render_draw_data()
{
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
void gr_opengl_init_function_pointers()
{
gr_screen.gf_flip = gr_opengl_flip;
@@ -1173,6 +1185,9 @@ void gr_opengl_init_function_pointers()
gr_screen.gf_override_fog = gr_opengl_override_fog;
gr_screen.gf_imgui_new_frame = gr_opengl_imgui_new_frame;
gr_screen.gf_imgui_render_draw_data = gr_opengl_imgui_render_draw_data;
gr_screen.gf_openxr_get_extensions = gr_opengl_openxr_get_extensions;
gr_screen.gf_openxr_test_capabilities = gr_opengl_openxr_test_capabilities;
gr_screen.gf_openxr_create_session = gr_opengl_openxr_create_session;
@@ -1600,12 +1615,19 @@ bool gr_opengl_is_capable(gr_capability capability)
return GLAD_GL_ARB_buffer_storage != 0;
case gr_capability::CAPABILITY_BPTC:
return GLAD_GL_ARB_texture_compression_bptc != 0;
case gr_capability::CAPABILITY_S3TC:
return GLAD_GL_EXT_texture_compression_s3tc != 0;
case gr_capability::CAPABILITY_LARGE_SHADER:
return !Cmdline_no_large_shaders;
case gr_capability::CAPABILITY_INSTANCED_RENDERING:
return GLAD_GL_ARB_vertex_attrib_binding;
case gr_capability::CAPABILITY_FAST_SHADOWS:
return GLAD_GL_ARB_vertex_attrib_binding && GLAD_GL_ARB_shader_viewport_layer_array && GLAD_GL_ARB_gpu_shader5;
case gr_capability::CAPABILITY_QUERIES_REUSABLE:
return true;
case gr_capability::CAPABILITY_RAYTRACED_SHADOWS:
// Raytraced shadows are only implemented for the Vulkan backend.
return false;
}
@@ -1676,7 +1698,7 @@ DCF(ogl_anisotropy, "toggles anisotropic filtering")
bool process = true;
int value;
if ( gr_screen.mode != GR_OPENGL ) {
if ( gr_screen.mode != GraphicsAPI::OpenGL ) {
dc_printf("Can only set anisotropic filter in OpenGL mode.\n");
return;
}
+14 -137
View File
@@ -1,7 +1,7 @@
#include "gropengldeferred.h"
#include "globalincs/vmallocator.h"
#include "graphics/util/primitives.h"
#include "ShaderProgram.h"
#include "gropengldraw.h"
@@ -375,7 +375,7 @@ void gr_opengl_deferred_lighting_finish()
auto light_data = prepare_light_uniforms(l, light_uniform_aligner, lp);
if (l.type == Light_Type::Cone) {
light_data->dualCone = (l.flags & LF_DUAL_CONE) ? 1.0f : 0.0f;
light_data->dualCone = (l.flags & LF_DUAL_CONE) ? 1 : 0;
light_data->coneAngle = l.cone_angle;
light_data->coneInnerAngle = l.cone_inner_angle;
light_data->coneDir = l.vec2;
@@ -703,69 +703,12 @@ void gr_opengl_draw_deferred_light_sphere(const vec3d *position)
}
void gr_opengl_deferred_light_cylinder_init(int segments) // Generate a VBO of a cylinder of radius and height 1.0f, based on code at http://www.ogre3d.org/tikiwiki/ManualSphereMeshes
void gr_opengl_deferred_light_cylinder_init(int segments)
{
unsigned int nVertex = (segments + 1) * 2 * 3 + 6; // Can someone verify this?
unsigned int nIndex = deferred_light_cylinder_icount = 12 * (segments + 1) - 6; //This too
float *Vertices = (float*)vm_malloc(sizeof(float) * nVertex);
float *pVertex = Vertices;
ushort *Indices = (ushort*)vm_malloc(sizeof(ushort) * nIndex);
ushort *pIndex = Indices;
auto mesh = graphics::util::generate_cylinder_mesh(segments);
float fDeltaSegAngle = (2.0f * PI / segments);
unsigned short wVerticeIndex = 0 ;
*pVertex++ = 0.0f;
*pVertex++ = 0.0f;
*pVertex++ = 0.0f;
wVerticeIndex ++;
*pVertex++ = 0.0f;
*pVertex++ = 0.0f;
*pVertex++ = 1.0f;
wVerticeIndex ++;
for( int ring = 0; ring <= 1; ring++ ) {
float z0 = (float)ring;
// Generate the group of segments for the current ring
for(int seg = 0; seg <= segments; seg++) {
float x0 = sinf(seg * fDeltaSegAngle);
float y0 = cosf(seg * fDeltaSegAngle);
// Add one vertex to the strip which makes up the cylinder
*pVertex++ = x0;
*pVertex++ = y0;
*pVertex++ = z0;
if (!ring) {
*pIndex++ = wVerticeIndex + (ushort)segments + 1;
*pIndex++ = wVerticeIndex;
*pIndex++ = wVerticeIndex + (ushort)segments;
*pIndex++ = wVerticeIndex + (ushort)segments + 1;
*pIndex++ = wVerticeIndex + 1;
*pIndex++ = wVerticeIndex;
if(seg != segments)
{
*pIndex++ = wVerticeIndex + 1;
*pIndex++ = wVerticeIndex;
*pIndex++ = 0;
}
wVerticeIndex ++;
}
else
{
if(seg != segments)
{
*pIndex++ = wVerticeIndex + 1;
*pIndex++ = wVerticeIndex;
*pIndex++ = 1;
wVerticeIndex ++;
}
}
}; // end for seg
} // end for ring
deferred_light_cylinder_vcount = wVerticeIndex;
deferred_light_cylinder_vcount = static_cast<GLushort>(mesh.vertex_count);
deferred_light_cylinder_icount = mesh.index_count;
glGetError();
@@ -774,17 +717,12 @@ void gr_opengl_deferred_light_cylinder_init(int segments) // Generate a VBO of a
// make sure we have one
if (deferred_light_cylinder_vbo) {
glBindBuffer(GL_ARRAY_BUFFER, deferred_light_cylinder_vbo);
glBufferData(GL_ARRAY_BUFFER, nVertex * sizeof(float), Vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(float), mesh.vertices.data(), GL_STATIC_DRAW);
// just in case
if ( opengl_check_for_errors() ) {
glDeleteBuffers(1, &deferred_light_cylinder_vbo);
deferred_light_cylinder_vbo = 0;
vm_free(Indices);
Indices = nullptr;
vm_free(Vertices);
Vertices = nullptr;
return;
}
@@ -796,71 +734,25 @@ void gr_opengl_deferred_light_cylinder_init(int segments) // Generate a VBO of a
// make sure we have one
if (deferred_light_cylinder_ibo) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, deferred_light_cylinder_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, nIndex * sizeof(ushort), Indices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(ushort), mesh.indices.data(), GL_STATIC_DRAW);
// just in case
if ( opengl_check_for_errors() ) {
glDeleteBuffers(1, &deferred_light_cylinder_ibo);
deferred_light_cylinder_ibo = 0;
vm_free(Indices);
Indices = nullptr;
vm_free(Vertices);
Vertices = nullptr;
return;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
vm_free(Indices);
Indices = nullptr;
vm_free(Vertices);
Vertices = nullptr;
}
void gr_opengl_deferred_light_sphere_init(int rings, int segments) // Generate a VBO of a sphere of radius 1.0f, based on code at http://www.ogre3d.org/tikiwiki/ManualSphereMeshes
void gr_opengl_deferred_light_sphere_init(int rings, int segments)
{
unsigned int nVertex = (rings + 1) * (segments+1) * 3;
unsigned int nIndex = deferred_light_sphere_icount = 6 * rings * (segments + 1);
float *Vertices = (float*)vm_malloc(sizeof(float) * nVertex);
float *pVertex = Vertices;
ushort *Indices = (ushort*)vm_malloc(sizeof(ushort) * nIndex);
ushort *pIndex = Indices;
auto mesh = graphics::util::generate_sphere_mesh(rings, segments);
float fDeltaRingAngle = (PI / rings);
float fDeltaSegAngle = (2.0f * PI / segments);
unsigned short wVerticeIndex = 0 ;
// Generate the group of rings for the sphere
for( int ring = 0; ring <= rings; ring++ ) {
float r0 = sinf (ring * fDeltaRingAngle);
float y0 = cosf (ring * fDeltaRingAngle);
// Generate the group of segments for the current ring
for(int seg = 0; seg <= segments; seg++) {
float x0 = r0 * sinf(seg * fDeltaSegAngle);
float z0 = r0 * cosf(seg * fDeltaSegAngle);
// Add one vertex to the strip which makes up the sphere
*pVertex++ = x0;
*pVertex++ = y0;
*pVertex++ = z0;
if (ring != rings) {
// each vertex (except the last) has six indices pointing to it
*pIndex++ = wVerticeIndex + (ushort)segments + 1;
*pIndex++ = wVerticeIndex;
*pIndex++ = wVerticeIndex + (ushort)segments;
*pIndex++ = wVerticeIndex + (ushort)segments + 1;
*pIndex++ = wVerticeIndex + 1;
*pIndex++ = wVerticeIndex;
wVerticeIndex ++;
}
}; // end for seg
} // end for ring
deferred_light_sphere_vcount = wVerticeIndex;
deferred_light_sphere_vcount = static_cast<GLushort>(mesh.vertex_count);
deferred_light_sphere_icount = mesh.index_count;
glGetError();
@@ -869,17 +761,12 @@ void gr_opengl_deferred_light_sphere_init(int rings, int segments) // Generate a
// make sure we have one
if (deferred_light_sphere_vbo) {
glBindBuffer(GL_ARRAY_BUFFER, deferred_light_sphere_vbo);
glBufferData(GL_ARRAY_BUFFER, nVertex * sizeof(float), Vertices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(float), mesh.vertices.data(), GL_STATIC_DRAW);
// just in case
if ( opengl_check_for_errors() ) {
glDeleteBuffers(1, &deferred_light_sphere_vbo);
deferred_light_sphere_vbo = 0;
vm_free(Vertices);
Vertices = nullptr;
vm_free(Indices);
Indices = nullptr;
return;
}
@@ -891,27 +778,17 @@ void gr_opengl_deferred_light_sphere_init(int rings, int segments) // Generate a
// make sure we have one
if (deferred_light_sphere_ibo) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, deferred_light_sphere_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, nIndex * sizeof(ushort), Indices, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.indices.size() * sizeof(ushort), mesh.indices.data(), GL_STATIC_DRAW);
// just in case
if ( opengl_check_for_errors() ) {
glDeleteBuffers(1, &deferred_light_sphere_ibo);
deferred_light_sphere_ibo = 0;
vm_free(Vertices);
Vertices = nullptr;
vm_free(Indices);
Indices = nullptr;
return;
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
vm_free(Vertices);
Vertices = nullptr;
vm_free(Indices);
Indices = nullptr;
}
void opengl_draw_sphere()
+1
View File
@@ -11,6 +11,7 @@
// the other platforms do not go before the #includes because this will cause conflicts from symbols defined in XLib,
// specifically None and Always; see https://stackoverflow.com/questions/22476110/c-compiling-error-including-x11-x-h-x11-xlib-h
#include "cmdline/cmdline.h"
#include "io/cursor.h"
#include "io/mouse.h"
#include "graphics/matrix.h"
@@ -12,6 +12,7 @@
#include "cmdline/cmdline.h"
#include "def_files/def_files.h"
#include "graphics/shader_types.h"
#include "graphics/grinternal.h"
#include "graphics/openxr.h"
#include "graphics/util/uniform_structs.h"
@@ -88,7 +89,11 @@ void opengl_post_pass_tonemap()
data->x0 = ppc.x0;
data->x1 = ppc.x1;
data->y0 = ppc.y0;
data->exposure = ltp::current_exposure(); });
data->exposure = ltp::current_exposure();
// OpenGL never negotiates HDR10 output, so Gr_hdr_output_active is
// always false here and current_tonemapper() never returns HdrScene.
data->hdr_paperwhite_nits = 0.0f;
data->hdr_peak_nits = 0.0f; });
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, Scene_ldr_texture, 0);
@@ -785,71 +790,9 @@ void gr_opengl_post_process_restore_zbuffer()
}
}
static void set_fxaa_defines(SCP_stringstream& sflags)
{
// Since we require OpenGL 3.2 we always have support for GLSL 130
sflags << "#define FXAA_GLSL_120 0\n";
sflags << "#define FXAA_GLSL_130 1\n";
if (GLSL_version >= 400) {
// The gather function became part of the standard with GLSL 4.00
sflags << "#define FXAA_GATHER4_ALPHA 1\n";
}
switch (Gr_aa_mode) {
case AntiAliasMode::None:
sflags << "#define FXAA_QUALITY_PRESET 10\n";
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/6.0)\n";
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/12.0)\n";
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
break;
case AntiAliasMode::FXAA_Low:
sflags << "#define FXAA_QUALITY_PRESET 12\n";
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/8.0)\n";
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/16.0)\n";
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
break;
case AntiAliasMode::FXAA_Medium:
sflags << "#define FXAA_QUALITY_PRESET 26\n";
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/12.0)\n";
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/24.0)\n";
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
break;
case AntiAliasMode::FXAA_High:
sflags << "#define FXAA_QUALITY_PRESET 39\n";
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/15.0)\n";
sflags << "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/32.0)\n";
sflags << "#define FXAA_QUALITY_SUBPIX 0.33\n";
break;
default:
UNREACHABLE("Unhandled FXAA mode %d!", static_cast<int>(Gr_aa_mode));
}
}
void set_smaa_defines(SCP_stringstream& sflags)
{
// Define what GLSL version we use
if (GLSL_version >= 400) {
sflags << "#define SMAA_GLSL_4\n";
} else {
sflags << "#define SMAA_GLSL_3\n";
}
switch (Gr_aa_mode) {
case AntiAliasMode::SMAA_Low:
sflags << "#define SMAA_PRESET_LOW\n";
break;
case AntiAliasMode::SMAA_Medium:
sflags << "#define SMAA_PRESET_MEDIUM\n";
break;
case AntiAliasMode::SMAA_High:
sflags << "#define SMAA_PRESET_HIGH\n";
break;
case AntiAliasMode::SMAA_Ultra:
sflags << "#define SMAA_PRESET_ULTRA\n";
break;
default:
UNREACHABLE("Unhandled SMAA mode %d!", static_cast<int>(Gr_aa_mode));
}
sflags << shader_get_smaa_defines(Gr_aa_mode, GLSL_version >= 400).c_str();
}
void opengl_post_shader_header(SCP_stringstream& sflags, shader_type shader_t, int flags)
{
@@ -868,7 +811,7 @@ void opengl_post_shader_header(SCP_stringstream& sflags, shader_type shader_t, i
snprintf(temp, 64, "#define SAMPLE_NUM %d\n", ls_params.samplenum);
sflags << temp;
} else if (shader_t == SDR_TYPE_POST_PROCESS_FXAA) {
set_fxaa_defines(sflags);
sflags << shader_get_fxaa_defines(Gr_aa_mode, GLSL_version >= 400);
} else if (shader_t == SDR_TYPE_POST_PROCESS_SMAA_EDGE || shader_t == SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT ||
shader_t == SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING) {
set_smaa_defines(sflags);
@@ -1217,4 +1160,4 @@ void opengl_post_process_shutdown()
Post_active_shader_index = 0;
Post_initialized = 0;
}
}
+28 -422
View File
@@ -12,6 +12,7 @@
#include "ShaderProgram.h"
#include "cfile/cfile.h"
#include "graphics/shader_preprocess.h"
#include "cmdline/cmdline.h"
#include "def_files/def_files.h"
#include "graphics/2d.h"
@@ -22,6 +23,7 @@
#include "graphics/opengl/gropenglstate.h"
#include "graphics/opengl/gropengltexture.h"
#include "graphics/opengl/gropengltnl.h"
#include "graphics/shader_types.h"
#include "graphics/util/uniform_structs.h"
#include "lighting/lighting.h"
#include "math/vecmat.h"
@@ -86,150 +88,6 @@ opengl_uniform_block_binding GL_uniform_blocks[] = {
* Static lookup reference for shader uniforms
* When adding a new shader, list all associated uniforms and attributes here
*/
// clang-format off
static opengl_shader_type_t GL_shader_types[] = {
{ SDR_TYPE_MODEL, "main-v.sdr", "main-f.sdr", "main-g.sdr",
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::NORMAL, opengl_vert_attrib::TANGENT, opengl_vert_attrib::MODEL_ID }, "Model Rendering", false },
{ SDR_TYPE_EFFECT_PARTICLE, "effect-v.sdr", "effect-f.sdr", "effect-g.sdr",
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::RADIUS, opengl_vert_attrib::COLOR }, "Particle Effects", false },
{ SDR_TYPE_EFFECT_DISTORTION, "effect-distort-v.sdr", "effect-distort-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::RADIUS, opengl_vert_attrib::COLOR }, "Distortion Effects", false },
{ SDR_TYPE_POST_PROCESS_MAIN, "post-v.sdr", "post-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Post Processing", false },
{ SDR_TYPE_POST_PROCESS_BLUR, "post-v.sdr", "blur-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Gaussian Blur", false },
{ SDR_TYPE_POST_PROCESS_BLOOM_COMP, "post-v.sdr", "bloom-comp-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Bloom Compositing", false },
{ SDR_TYPE_POST_PROCESS_BRIGHTPASS, "post-v.sdr", "brightpass-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Bloom Brightpass", false },
{ SDR_TYPE_POST_PROCESS_FXAA, "fxaa-v.sdr", "fxaa-f.sdr", 0,
{ opengl_vert_attrib::POSITION }, "FXAA", false },
{ SDR_TYPE_POST_PROCESS_FXAA_PREPASS, "post-v.sdr", "fxaapre-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "FXAA Prepass", false },
{ SDR_TYPE_POST_PROCESS_LIGHTSHAFTS, "post-v.sdr", "ls-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Lightshafts", false },
{ SDR_TYPE_POST_PROCESS_TONEMAPPING, "post-v.sdr", "tonemapping-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Tonemapping", false },
{ SDR_TYPE_DEFERRED_LIGHTING, "deferred-v.sdr", "deferred-f.sdr", 0,
{ opengl_vert_attrib::POSITION }, "Deferred Lighting", false },
{ SDR_TYPE_DEFERRED_CLEAR, "deferred-clear-v.sdr", "deferred-clear-f.sdr", 0,
{ opengl_vert_attrib::POSITION }, "Clear Deferred Lighting Buffer", false },
{ SDR_TYPE_VIDEO_PROCESS, "video-v.sdr", "video-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Video Playback", false },
{ SDR_TYPE_PASSTHROUGH_RENDER, "passthrough-v.sdr", "passthrough-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::COLOR }, "Passthrough", false },
{ SDR_TYPE_SHIELD_DECAL, "shield-impact-v.sdr", "shield-impact-f.sdr", 0,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::NORMAL }, "Shield Decals", false },
{ SDR_TYPE_BATCHED_BITMAP, "batched-v.sdr", "batched-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::COLOR }, "Batched bitmaps", false },
{ SDR_TYPE_DEFAULT_MATERIAL, "default-material.vert.spv.glsl", "default-material.frag.spv.glsl", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::COLOR }, "Default material", true },
{ SDR_TYPE_NANOVG, "nanovg-v.sdr", "nanovg-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "NanoVG shader", false },
{ SDR_TYPE_DECAL, "decal-v.sdr", "decal-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::MODEL_MATRIX }, "Decal rendering", false },
{ SDR_TYPE_SCENE_FOG, "post-v.sdr", "fog-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Scene fogging", false },
{ SDR_TYPE_VOLUMETRIC_FOG, "post-v.sdr", "volumetric-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Volumetric fogging", false },
{ SDR_TYPE_ROCKET_UI, "rocketui-v.sdr", "rocketui-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::COLOR, opengl_vert_attrib::TEXCOORD }, "libRocket UI", false },
{ SDR_TYPE_COPY, "post-v.sdr", "copy-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Texture copy", false },
{ SDR_TYPE_COPY_WORLD, "passthrough-v.sdr", "copy-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Texture copy world space", false },
{ SDR_TYPE_MSAA_RESOLVE, "post-v.sdr", "msaa-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "MSAA resolve shader", false },
{ SDR_TYPE_POST_PROCESS_SMAA_EDGE, "smaa-edge-v.sdr", "smaa-edge-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "SMAA Edge detection", false },
{ SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT, "smaa-blend-v.sdr", "smaa-blend-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "SMAA Blending weight calculation", false },
{ SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING, "smaa-neighbour-v.sdr", "smaa-neighbour-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "SMAA Neighborhood Blending", false },
{ SDR_TYPE_ENVMAP_SPHERE_WARP, "post-v.sdr", "envmap-sphere-warp-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Environment Map Export", false },
{ SDR_TYPE_IRRADIANCE_MAP_GEN, "post-v.sdr", "irrmap-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Irradiance Map Generation", false },
{ SDR_TYPE_SHADOW_MAP_GEN, "shadow_map-v.sdr", "shadow_map-f.sdr", "shadow_map-g.sdr",
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD, opengl_vert_attrib::NORMAL, opengl_vert_attrib::TANGENT, opengl_vert_attrib::MODEL_ID }, "Shadow Map Generation", false },
{ SDR_TYPE_GAMMA_BLIT, "post-v.sdr", "gamma-correct-f.sdr", nullptr,
{ opengl_vert_attrib::POSITION, opengl_vert_attrib::TEXCOORD }, "Gamma correct blit", false },
};
// clang-format on
/**
* Static lookup reference for shader variant uniforms
* When adding a new shader variant for a shader, list all associated uniforms and attributes here
*/
static opengl_shader_variant_t GL_shader_variants[] = {
//Model shader flags, both those set always as a compile flag as well as those that are usually runtime checks, need to be defined in this file
#define MODEL_SDR_FLAG_MODE_CPP_ARRAY
#include "def_files/data/effects/model_shader_flags.h"
#undef MODEL_SDR_FLAG_MODE_CPP_ARRAY
{SDR_TYPE_EFFECT_PARTICLE, true, SDR_FLAG_PARTICLE_POINT_GEN, "FLAG_EFFECT_GEOMETRY", {opengl_vert_attrib::UVEC}, "Geometry shader point-based particles"},
{SDR_TYPE_DEFERRED_LIGHTING, false, SDR_FLAG_ENV_MAP, "ENV_MAP", {}, "Render ambient light with env and irrmaps"},
{SDR_TYPE_POST_PROCESS_BLUR, false, SDR_FLAG_BLUR_HORIZONTAL, "PASS_0", {}, "Horizontal blur pass"},
{SDR_TYPE_POST_PROCESS_BLUR, false, SDR_FLAG_BLUR_VERTICAL, "PASS_1", {}, "Vertical blur pass"},
{SDR_TYPE_NANOVG, false, SDR_FLAG_NANOVG_EDGE_AA, "EDGE_AA", {}, "NanoVG edge anti-alias"},
{SDR_TYPE_DECAL, false, SDR_FLAG_DECAL_USE_NORMAL_MAP, "USE_NORMAL_MAP", {}, "Decal use scene normal map"},
{SDR_TYPE_MSAA_RESOLVE, false, SDR_FLAG_MSAA_SAMPLES_4, "SAMPLES_4", {}, "Sets the MSAA resolve shader to 4 samples"},
{SDR_TYPE_MSAA_RESOLVE, false, SDR_FLAG_MSAA_SAMPLES_8, "SAMPLES_8", {}, "Sets the MSAA resolve shader to 8 samples"},
{SDR_TYPE_MSAA_RESOLVE, false, SDR_FLAG_MSAA_SAMPLES_16, "SAMPLES_16", {}, "Sets the MSAA resolve shader to 16 samples"},
{SDR_TYPE_VOLUMETRIC_FOG, false, SDR_FLAG_VOLUMETRICS_DO_EDGE_SMOOTHING, "DO_EDGE_SMOOTHING", {}, "Perform costly edge smoothing lookups"},
{SDR_TYPE_VOLUMETRIC_FOG, false, SDR_FLAG_VOLUMETRICS_NOISE, "NOISE", {}, "Add noise to volumetrics"},
{SDR_TYPE_COPY_WORLD, false, SDR_FLAG_COPY_FROM_ARRAY, "COPY_ARRAY", {}, "Expects to copy from an array texture"},
{SDR_TYPE_POST_PROCESS_TONEMAPPING, false, SDR_FLAG_TONEMAPPING_LINEAR_OUT, "LINEAR_OUT", {}, "Will make the tonemapper output in linear color space and not in sRGB"},
{SDR_TYPE_SHADOW_MAP_GEN, true, SDR_FLAG_SHADOW_FALLBACK, "GEOMETRY_FALLBACK", {}, "Will make the shadow map generation shader fall back on geometry shaders if multi viewports are not avilable"}
};
static const int GL_num_shader_variants = sizeof(GL_shader_variants) / sizeof(opengl_shader_variant_t);
opengl_shader_t *Current_shader = NULL;
@@ -323,6 +181,10 @@ static SCP_string opengl_shader_get_header(shader_type type_id, int flags, bool
#else
sflags << "#version " << GLSL_version << " es\n";
#endif
sflags << "#define OPENGL\n";
sflags << shader_get_shadow_cascade_defines();
sflags << shader_get_rt_shadow_light_limit_define();
if (Detail.lighting < 3) {
sflags << "#define FLAG_LIGHT_MODEL_BLINN_PHONG\n";
}
@@ -339,13 +201,7 @@ static SCP_string opengl_shader_get_header(shader_type type_id, int flags, bool
// ignore looking for variants. main post process, lightshafts, and FXAA shaders need special headers to be hacked in
opengl_post_shader_header(sflags, type_id, flags);
} else {
for (int i = 0; i < GL_num_shader_variants; ++i) {
opengl_shader_variant_t &variant = GL_shader_variants[i];
if (type_id == variant.type_id && flags & variant.flag) {
sflags << "#define " << variant.flag_text << "\n";
}
}
sflags << shader_build_variant_defines(type_id, flags);
}
return sflags.str();
@@ -361,258 +217,17 @@ static SCP_string opengl_shader_get_header(shader_type type_id, int flags, bool
* @param flags integer variable holding a combination of SDR_* flags
* @return C-string holding the complete shader source code
*/
static SCP_string opengl_load_shader(const char* filename) {
SCP_string content;
if (Enable_external_shaders) {
CFILE* cf_shader = cfopen(filename, "rt", CF_TYPE_EFFECTS);
if (cf_shader != NULL) {
int len = cfilelength(cf_shader);
content.resize(len);
cfread(&content[0], len + 1, 1, cf_shader);
cfclose(cf_shader);
return content;
}
}
//If we're still here, proceed with internals
nprintf(("shaders"," Loading built-in default shader for: %s\n", filename));
auto def_shader = defaults_get_file(filename);
content.assign(reinterpret_cast<const char*>(def_shader.data), def_shader.size);
return content;
}
static void handle_includes_impl(SCP_vector<SCP_string>& include_stack,
SCP_stringstream& output,
int& include_counter,
const SCP_string& filename,
const SCP_string& original) {
include_stack.emplace_back(filename);
auto current_source_number = include_counter + 1;
const char* INCLUDE_STRING = "#include";
const char* CONDITIONAL_INCLUDE_STRING = "#conditional_include";
SCP_stringstream input(original);
int line_num = 1;
for (SCP_string line; std::getline(input, line);) {
auto include_start = line.find(CONDITIONAL_INCLUDE_STRING);
if (include_start != SCP_string::npos) {
//This is a conditional include. Figure out whether to include, or whether not to.
// Conditional include syntax: #conditional_include (+|-)"capability" "filename"
// On +, include if capability is available, on -, include if not available
include_start += strlen(CONDITIONAL_INCLUDE_STRING) + 1;
bool require_capability = true;
switch(line.at(include_start)) {
case '+':
require_capability = true;
break;
case '-':
require_capability = false;
break;
default:
Error(LOCATION,
"Shader %s:%d: Malformed conditional_include line. Expected + or -, got %c.",
filename.c_str(),
line_num,
line.at(include_start));
break;
}
auto first_quote = line.find('"', include_start);
auto second_quote = line.find('"', first_quote + 1);
if (first_quote == SCP_string::npos || second_quote == SCP_string::npos) {
Error(LOCATION,
"Shader %s:%d: Malformed conditional_include line. Could not find both quote characters for capability.",
filename.c_str(),
line_num);
}
auto condition = line.substr(first_quote + 1, second_quote - first_quote - 1);
auto capability = std::find_if(&gr_capabilities[0], &gr_capabilities[gr_capabilities_num],
[condition](const gr_capability_def &ext_pair) { return !stricmp(ext_pair.parse_name, condition.c_str()); });
if (capability == &gr_capabilities[gr_capabilities_num]) {
Error(LOCATION,
"Shader %s:%d: Malformed conditional_include line. Capability %s does not exist.",
filename.c_str(),
line_num,
condition.c_str());
}
//Prepare for including if capability is correct, skip otherwise.
if(gr_is_capable(capability->capability) == require_capability)
include_start = second_quote + 1 - strlen(INCLUDE_STRING);
else
include_start = SCP_string::npos - 1;
}
else {
//Only search for normal includes if it's not a conditional include.
include_start = line.find(INCLUDE_STRING);
}
if (include_start != SCP_string::npos && include_start != SCP_string::npos - 1) {
auto first_quote = line.find('"', include_start + strlen(INCLUDE_STRING));
auto second_quote = line.find('"', first_quote + 1);
if (first_quote == SCP_string::npos || second_quote == SCP_string::npos) {
Error(LOCATION,
"Shader %s:%d: Malformed include line. Could not find both quote characters.",
filename.c_str(),
line_num);
}
auto file_name = line.substr(first_quote + 1, second_quote - first_quote - 1);
auto existing_name = std::find_if(include_stack.begin(), include_stack.end(), [&file_name](const SCP_string& str) {
return str == file_name;
});
if (existing_name != include_stack.end()) {
SCP_stringstream stack_string;
for (auto& name : include_stack) {
stack_string << "\t" << name << "\n";
}
Error(LOCATION,
"Shader %s:%d: Detected cyclic include! Previous includes (top level file first):\n%s",
filename.c_str(),
line_num,
stack_string.str().c_str());
}
++include_counter;
// The second parameter defines which source string we are currently working with. We keep track of how many
// excludes have been in the file so far to specify this
output << "#line 1 " << include_counter + 1 << "\n";
handle_includes_impl(include_stack,
output,
include_counter,
file_name,
opengl_load_shader(file_name.c_str()));
// We are done with the include file so now we can return to the original file
output << "#line " << line_num + 1 << " " << current_source_number << "\n";
} else if (include_start != SCP_string::npos - 1) {
output << line << "\n";
}
++line_num;
}
include_stack.pop_back();
}
static SCP_string handle_includes(const char* filename, const SCP_string& original) {
SCP_stringstream output;
SCP_vector<SCP_string> include_stack;
auto include_counter = 0;
handle_includes_impl(include_stack, output, include_counter, filename, original);
return output.str();
}
static SCP_string handle_predefines(const char* filename, const SCP_string& original){
SCP_stringstream output;
SCP_unordered_map<SCP_string, SCP_string> defines;
output << "#define GLOBAL_FAR_Z " << std::fixed << std::setprecision(2) << Max_draw_distance << std::defaultfloat << '\n';
output << "#define NUM_SHADOW_CASCADES " << (Num_shadow_cascades + Num_cockpit_shadow_cascades) << '\n';
const char* PREDEFINE_STRING = "#predefine";
const char* PREREPLACE_STRING = "#prereplace";
SCP_stringstream input(original);
for (SCP_string line; std::getline(input, line);) {
auto predefine_start = line.find(PREDEFINE_STRING);
auto prereplace_start = line.find(PREREPLACE_STRING);
if (predefine_start != SCP_string::npos){
predefine_start += strlen(PREDEFINE_STRING);
auto token_start = line.find(' ', predefine_start);
auto token_end = line.find(' ', token_start + 1);
if (token_start == SCP_string::npos || token_end == SCP_string::npos) {
Error(LOCATION,
"Shader %s: Malformed predefine line. Could not find define token.",
filename);
}
auto token = line.substr(token_start + 1, token_end - token_start - 1);
auto replaceWith = line.substr(token_end + 1);
auto replaceStrToken = replaceWith.find("%s");
if (replaceStrToken == SCP_string::npos || replaceWith.find("%s", replaceStrToken + 1) != SCP_string::npos){
Error(LOCATION,
"Shader %s: Malformed predefine line. Replacing string must have exactly one %%s.",
filename);
}
if (defines.find(token) != defines.end()) {
Error(LOCATION,
"Shader %s: Malformed predefine line. Token %s is already defined.",
filename,
token.c_str());
}
defines.emplace(std::move(token), std::move(replaceWith));
output << "\n"; //At this point, don't mess with the linecount
}
else if (prereplace_start != SCP_string::npos){
prereplace_start += strlen(PREREPLACE_STRING);
auto token_start = line.find(' ', prereplace_start);
auto token_end = line.find(' ', token_start + 1);
if (token_start == SCP_string::npos || token_end == SCP_string::npos) {
Error(LOCATION,
"Shader %s: Malformed prereplace line. Could not find define token.",
filename);
}
auto token = line.substr(token_start + 1, token_end - token_start - 1);
auto replaceArg = line.substr(token_end + 1);
auto replaceWithIt = defines.find(token);
if (replaceWithIt == defines.end()) {
Error(LOCATION,
"Shader %s: Malformed prereplace line. Could not find token %s.",
filename,
token.c_str());
}
size_t size = replaceWithIt->second.length() - 1 + replaceArg.size();
std::unique_ptr<char[]> buffer = make_unique<char[]>(size);
snprintf(buffer.get(), size, replaceWithIt->second.c_str(), replaceArg.c_str());
buffer[size - 1] = '\0';
output << buffer.get() << "\n";
}
else {
output << line << "\n";
}
}
return output.str();
}
static SCP_vector<SCP_string>
opengl_get_shader_content(shader_type type_id, const char* filename, int flags, bool has_geo_shader, bool spirv_shader)
{
SCP_vector<SCP_string> parts;
if (spirv_shader) {
// No need to add a header here or handle includes since the original compiler did that
parts.push_back(opengl_load_shader(filename));
parts.push_back(shader_load_source(filename));
} else {
parts.push_back(opengl_shader_get_header(type_id, flags, has_geo_shader));
parts.push_back(handle_predefines(filename, handle_includes(filename, opengl_load_shader(filename))));
parts.push_back(shader_preprocess_defines(filename, shader_preprocess_includes(filename, shader_load_source(filename))));
}
return parts;
@@ -823,9 +438,9 @@ static void opengl_set_default_uniforms(const opengl_shader_t& sdr) {
void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_shader_t &new_shader)
{
opengl_shader_type_t *sdr_info = &GL_shader_types[sdr];
const ShaderTypeInfo *sdr_info = shader_get_type_info(sdr);
Assert(sdr_info->type_id == sdr);
Assert(sdr_info != nullptr);
nprintf(("shaders","Compiling new shader:\n"));
nprintf(("shaders"," %s\n", sdr_info->description));
@@ -833,27 +448,24 @@ void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_sha
bool use_geo_sdr = false;
// do we even have a geometry shader?
if (sdr_info->geo != NULL) {
for (int i = 0; i < GL_num_shader_variants; ++i) {
opengl_shader_variant_t *variant = &GL_shader_variants[i];
if (variant->type_id == sdr && flags & variant->flag && variant->use_geometry_sdr) {
if (sdr_info->geo != nullptr) {
shader_for_each_active_variant(sdr, flags, [&](const ShaderVariantInfo& variant) {
if (variant.use_geometry_sdr) {
use_geo_sdr = true;
break;
}
}
});
}
auto vert_content =
opengl_get_shader_content(sdr_info->type_id, sdr_info->vert, flags, use_geo_sdr, sdr_info->spirv_shader);
opengl_get_shader_content(sdr, sdr_info->vert, flags, use_geo_sdr, sdr_info->spirv_shader);
auto frag_content =
opengl_get_shader_content(sdr_info->type_id, sdr_info->frag, flags, use_geo_sdr, sdr_info->spirv_shader);
opengl_get_shader_content(sdr, sdr_info->frag, flags, use_geo_sdr, sdr_info->spirv_shader);
SCP_vector<SCP_string> geom_content;
if (use_geo_sdr) {
// read geometry shader
geom_content =
opengl_get_shader_content(sdr_info->type_id, sdr_info->geo, flags, use_geo_sdr, sdr_info->spirv_shader);
opengl_get_shader_content(sdr, sdr_info->geo, flags, use_geo_sdr, sdr_info->spirv_shader);
}
auto shader_hash = get_shader_hash(vert_content, geom_content, frag_content);
@@ -870,7 +482,7 @@ void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_sha
for (size_t i = 0; i < GL_vertex_attrib_info.size(); ++i) {
// Check that the enum values match the position in the vector to make accessing that information more efficient
Assertion(GL_vertex_attrib_info[i].attribute_id == (int)i, "Mistmatch between enum values and attribute vector detected!");
Assertion(GL_vertex_attrib_info[i].attribute_id == i, "Mistmatch between enum values and attribute vector detected!");
// assign vert attribute binding locations before we link the shader
glBindAttribLocation(program->getShaderHandle(), (GLint)i, GL_vertex_attrib_info[i].name.c_str());
@@ -898,7 +510,7 @@ void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_sha
cache_program_binary(program->getShaderHandle(), shader_hash);
}
new_shader.shader = sdr_info->type_id;
new_shader.shader = sdr;
new_shader.flags = flags;
new_shader.program = std::move(program);
@@ -919,19 +531,13 @@ void opengl_compile_shader_actual(shader_type sdr, const uint &flags, opengl_sha
nprintf(("shaders","Shader Variant Features:\n"));
// initialize all uniforms and attributes that are specific to this variant
for (int i = 0; i < GL_num_shader_variants; ++i) {
opengl_shader_variant_t &variant = GL_shader_variants[i];
if (sdr_info->type_id == variant.type_id && variant.flag & flags) {
for (auto& attr : variant.attributes) {
auto& attr_info = GL_vertex_attrib_info[attr];
new_shader.program->initAttribute(attr_info.name, attr_info.default_value);
}
nprintf(("shaders"," %s\n", variant.description));
// initialize extra attributes from active variants
shader_for_each_active_variant(sdr, flags, [&](const ShaderVariantInfo& v) {
for (auto& attr : v.attributes) {
new_shader.program->initAttribute(GL_vertex_attrib_info[attr].name, GL_vertex_attrib_info[attr].default_value);
}
}
nprintf(("shaders"," %s\n", v.description));
});
opengl_set_default_uniforms(new_shader);
}
@@ -1116,8 +722,8 @@ void opengl_shader_set_default_material(bool textured, bool alpha, vec4* clr, fl
{
Current_shader->program->Uniforms.setTextureUniform("baseMap", 0);
opengl_set_generic_uniform_data<genericData_default_material_vert>(
[=](genericData_default_material_vert* data) {
opengl_set_generic_uniform_data<genericData_default_material_v_sdr>(
[=](genericData_default_material_v_sdr* data) {
if (textured) {
data->noTexturing = 0;
data->baseMapIndex = array_index;
+14 -39
View File
@@ -12,6 +12,7 @@
#include "globalincs/pstypes.h"
#include "graphics/2d.h"
#include "graphics/shader_types.h"
#include "graphics/material.h"
#include "graphics/opengl/gropengl.h"
#include "graphics/util/UniformBuffer.h"
@@ -26,18 +27,19 @@ class ShaderProgram;
enum shader_stage { SDR_STAGE_VERTEX, SDR_STAGE_FRAGMENT, SDR_STAGE_GEOMETRY };
struct opengl_vert_attrib {
enum attrib_id {
POSITION,
COLOR,
TEXCOORD,
NORMAL,
TANGENT,
MODEL_ID,
RADIUS,
UVEC,
MODEL_MATRIX,
NUM_ATTRIBS,
};
// Attribute location enum — aliases the shared VertexAttributeLocation enum
// from shader_types.h.
using attrib_id = VertexAttributeLocation;
static constexpr attrib_id POSITION = VATTRIB_POSITION;
static constexpr attrib_id COLOR = VATTRIB_COLOR;
static constexpr attrib_id TEXCOORD = VATTRIB_TEXCOORD;
static constexpr attrib_id NORMAL = VATTRIB_NORMAL;
static constexpr attrib_id TANGENT = VATTRIB_TANGENT;
static constexpr attrib_id MODEL_ID = VATTRIB_MODELID;
static constexpr attrib_id RADIUS = VATTRIB_RADIUS;
static constexpr attrib_id UVEC = VATTRIB_UVEC;
static constexpr attrib_id MODEL_MATRIX = VATTRIB_MODEL_MATRIX;
static constexpr attrib_id NUM_ATTRIBS = NUM_VERTEX_ATTRIBS;
attrib_id attribute_id;
SCP_string name;
@@ -60,33 +62,6 @@ struct geometry_sdr_params
int vertices_out;
};
struct opengl_shader_type_t {
shader_type type_id;
const char *vert;
const char *frag;
const char *geo;
SCP_vector<opengl_vert_attrib::attrib_id> attributes;
const char* description;
bool spirv_shader; // Specified if this shader was generated from SPIR-V
};
struct opengl_shader_variant_t {
shader_type type_id;
bool use_geometry_sdr;
int flag;
SCP_string flag_text;
SCP_vector<opengl_vert_attrib::attrib_id> attributes;
const char* description;
};
struct opengl_shader_file_t {
const char *vert;
const char *frag;
+8 -14
View File
@@ -421,30 +421,26 @@ static int opengl_texture_set_level(int bitmap_handle, int bitmap_type, int bmap
}
// check for compressed image types
auto block_size = 0;
auto bm_handle = bm_is_compressed(bitmap_handle);
switch (bm_handle) {
auto bm_type = bm_is_compressed(bitmap_handle);
auto block_size = dds_block_size(bm_type);
switch (bm_type) {
case DDS_DXT1:
case DDS_CUBEMAP_DXT1:
intFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
block_size = 8;
break;
case DDS_DXT3:
case DDS_CUBEMAP_DXT3:
intFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
block_size = 16;
break;
case DDS_DXT5:
case DDS_CUBEMAP_DXT5:
intFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
block_size = 16;
break;
case DDS_BC7:
intFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB;
block_size = 16;
break;
case KTX_ETC2_RGB:
@@ -453,7 +449,7 @@ static int opengl_texture_set_level(int bitmap_handle, int bitmap_type, int bmap
case KTX_ETC2_SRGBA_EAC:
case KTX_ETC2_RGB_A1:
case KTX_ETC2_SRGB_A1:
intFormat = ktx_map_ktx_format_to_gl_internal(bm_handle);
intFormat = ktx_map_ktx_format_to_gl_internal(bm_type);
block_size = ktx_etc_block_size(intFormat);
break;
}
@@ -474,8 +470,7 @@ static int opengl_texture_set_level(int bitmap_handle, int bitmap_type, int bmap
auto mipmap_h = bmap_h;
for (auto i = 0; i < mipmap_levels + base_level; i++) {
// size of data block (4x4)
dsize = ((mipmap_h + 3) / 4) * ((mipmap_w + 3) / 4) * block_size;
dsize = static_cast<GLsizei>(dds_compressed_mip_size(mipmap_w, mipmap_h, block_size));
if (i >= base_level) {
glCompressedTexSubImage3D(tSlot->texture_target, i - base_level, 0, 0, tSlot->array_index, mipmap_w,
@@ -601,8 +596,7 @@ static int opengl_texture_set_level(int bitmap_handle, int bitmap_type, int bmap
// check if it's a compressed cubemap first
if (block_size > 0) {
for (auto level = 0; level < mipmap_levels + base_level; level++) {
// size of data block (4x4)
dsize = ((mipmap_h + 3) / 4) * ((mipmap_w + 3) / 4) * block_size;
dsize = static_cast<GLsizei>(dds_compressed_mip_size(mipmap_w, mipmap_h, block_size));
if (level >= base_level) {
// We skipped ahead to the base level so we can start uploading frames now
@@ -1240,7 +1234,7 @@ int gr_opengl_tcache_set(int bitmap_handle, int bitmap_type, float *u_scale, flo
void opengl_preload_init()
{
if (gr_screen.mode != GR_OPENGL)
if (gr_screen.mode != GraphicsAPI::OpenGL)
return;
// opengl_tcache_flush ();
@@ -1251,7 +1245,7 @@ int gr_opengl_preload(int bitmap_num, int is_aabitmap)
float u_scale, v_scale;
int retval;
Assert( gr_screen.mode == GR_OPENGL );
Assert( gr_screen.mode == GraphicsAPI::OpenGL );
if ( !GL_should_preload ) {
return 0;
+4 -8
View File
@@ -77,7 +77,6 @@ static opengl_vertex_bind GL_array_binding_data[] =
{ vertex_format_data::POSITION4, 4, GL_FLOAT, GL_FALSE, opengl_vert_attrib::POSITION },
{ vertex_format_data::POSITION3, 3, GL_FLOAT, GL_FALSE, opengl_vert_attrib::POSITION },
{ vertex_format_data::POSITION2, 2, GL_FLOAT, GL_FALSE, opengl_vert_attrib::POSITION },
{ vertex_format_data::SCREEN_POS, 2, GL_INT, GL_FALSE, opengl_vert_attrib::POSITION },
{ vertex_format_data::COLOR3, 3, GL_UNSIGNED_BYTE, GL_TRUE, opengl_vert_attrib::COLOR },
{ vertex_format_data::COLOR4, 4, GL_UNSIGNED_BYTE, GL_TRUE, opengl_vert_attrib::COLOR },
{ vertex_format_data::COLOR4F, 4, GL_FLOAT, GL_FALSE, opengl_vert_attrib::COLOR },
@@ -371,8 +370,10 @@ void gr_opengl_delete_buffer(gr_buffer_handle handle)
glDeleteBuffers(1, &buffer_obj.buffer_id);
}
gr_buffer_handle gr_opengl_create_buffer(BufferType type, BufferUsageHint usage)
gr_buffer_handle gr_opengl_create_buffer(BufferType type, BufferUsageHint usage, bool /*rt_capable*/)
{
// rt_capable is a Vulkan-only concept (acceleration structure build input
// usage flags); OpenGL has no raytracing backend, so it is ignored here.
return opengl_create_buffer_object(convertBufferType(type), convertUsageHint(usage), usage);
}
@@ -1003,11 +1004,6 @@ void opengl_tnl_set_model_material(model_material *material_info)
if ( material_info->is_batched() ) {
GL_state.Texture.Enable(10, GL_TEXTURE_BUFFER, opengl_get_transform_buffer_texture());
}
if ( Deferred_lighting ) {
// don't blend if we're drawing to the g-buffers
GL_state.SetAlphaBlendMode(ALPHA_BLEND_NONE);
}
}
void opengl_tnl_set_material_particle(particle_material * material_info)
@@ -1332,4 +1328,4 @@ void opengl_bind_vertex_layout_multiple(vertex_layout &layout, const SCP_vector<
i++;
}
GL_state.Array.BindElementBuffer(indexBuffer);
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ struct opengl_vertex_bind {
opengl_vert_attrib::attrib_id attribute_id;
};
gr_buffer_handle gr_opengl_create_buffer(BufferType type, BufferUsageHint usage);
gr_buffer_handle gr_opengl_create_buffer(BufferType type, BufferUsageHint usage, bool rt_capable);
void opengl_bind_buffer_object(gr_buffer_handle handle);
void gr_opengl_update_buffer_data(gr_buffer_handle handle, size_t size, const void* data);
+4 -4
View File
@@ -260,7 +260,7 @@ void PostProcessingManager::setBloomShadersOk(bool ok)
bool gr_lightshafts_enabled()
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return false;
}
@@ -278,7 +278,7 @@ bool gr_lightshafts_enabled()
bool gr_sunglare_enabled()
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return false;
}
@@ -292,7 +292,7 @@ bool gr_sunglare_enabled()
int gr_bloom_intensity()
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return 0;
}
@@ -305,7 +305,7 @@ int gr_bloom_intensity()
void gr_set_bloom_intensity(int intensity)
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
+28 -27
View File
@@ -36,17 +36,18 @@ static void gr_flash_internal(int r, int g, int b, int a, bool alpha_flash)
render_material.set_blend_mode(ALPHA_BLEND_ALPHA_ADDITIVE);
}
int glVertices[8] = { x1, y1, x1, y2, x2, y1, x2, y2 };
float glVertices[8] = { (float)x1, (float)y1, (float)x1, (float)y2,
(float)x2, (float)y1, (float)x2, (float)y2 };
vertex_layout vert_def;
vert_def.add_vertex_component(vertex_format_data::SCREEN_POS, sizeof(int) * 2, 0);
vert_def.add_vertex_component(vertex_format_data::POSITION2, sizeof(float) * 2, 0);
gr_render_primitives_2d_immediate(&render_material, PRIM_TYPE_TRISTRIP, &vert_def, 4, glVertices, sizeof(int) * 8);
gr_render_primitives_2d_immediate(&render_material, PRIM_TYPE_TRISTRIP, &vert_def, 4, glVertices, sizeof(float) * 8);
}
void gr_flash(int r, int g, int b) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -58,7 +59,7 @@ void gr_flash(int r, int g, int b) {
}
void gr_flash_alpha(int r, int g, int b, int a) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -166,7 +167,7 @@ static void bitmap_ex_internal(int x,
}
void gr_aabitmap(int x, int y, int resize_mode, bool mirror, float scale_factor) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -253,7 +254,7 @@ void gr_aabitmap(int x, int y, int resize_mode, bool mirror, float scale_factor)
scale_factor);
}
void gr_aabitmap_ex(int x, int y, int w, int h, int sx, int sy, int resize_mode, bool mirror, float scale_factor) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -405,7 +406,7 @@ void gr_aabitmap_ex(int x, int y, int w, int h, int sx, int sy, int resize_mode,
}
//these are penguins bitmap functions
void gr_bitmap_ex(int x, int y, int w, int h, int sx, int sy, int resize_mode, bool mirror, float scale_factor) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -792,7 +793,7 @@ void endDrawing(graphics::paths::PathRenderer* path) {
void gr_string(float sx, float sy, const char* s, int resize_mode, float scaleMultiplier, size_t in_length)
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -995,7 +996,7 @@ static void gr_line(float x1, float y1, float x2, float y2, int resize_mode) {
}
void gr_line(int x1, int y1, int x2, int y2, int resize_mode) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1003,7 +1004,7 @@ void gr_line(int x1, int y1, int x2, int y2, int resize_mode) {
}
void gr_aaline(vertex* v1, vertex* v2) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1017,7 +1018,7 @@ void gr_aaline(vertex* v1, vertex* v2) {
}
void gr_gradient(int x1, int y1, int x2, int y2, int resize_mode) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1043,7 +1044,7 @@ void gr_gradient(int x1, int y1, int x2, int y2, int resize_mode) {
endDrawing(path);
}
void gr_pixel(int x, int y, int resize_mode) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1051,7 +1052,7 @@ void gr_pixel(int x, int y, int resize_mode) {
}
void gr_circle(int xc, int yc, int d, int resize_mode) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1064,7 +1065,7 @@ void gr_circle(int xc, int yc, int d, int resize_mode) {
endDrawing(path);
}
void gr_unfilled_circle(int xc, int yc, int d, int resize_mode) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1077,7 +1078,7 @@ void gr_unfilled_circle(int xc, int yc, int d, int resize_mode) {
endDrawing(path);
}
void gr_arc(int xc, int yc, float r, float angle_start, float angle_end, bool fill, int resize_mode) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1107,7 +1108,7 @@ void gr_arc(int xc, int yc, float r, float angle_start, float angle_end, bool fi
endDrawing(path);
}
void gr_curve(int xc, int yc, int r, int direction, int resize_mode) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1158,7 +1159,7 @@ void gr_curve(int xc, int yc, int r, int direction, int resize_mode) {
}
void gr_rect(int x, int y, int w, int h, int resize_mode, float angle) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1179,7 +1180,7 @@ void gr_rect(int x, int y, int w, int h, int resize_mode, float angle) {
}
void gr_shade(int x, int y, int w, int h, int resize_mode) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1201,7 +1202,7 @@ void gr_shade(int x, int y, int w, int h, int resize_mode) {
}
void gr_2d_start_buffer() {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1214,7 +1215,7 @@ void gr_2d_start_buffer() {
}
void gr_2d_stop_buffer() {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1232,7 +1233,7 @@ static size_t immediate_buffer_size = 0;
static const size_t IMMEDIATE_BUFFER_RESIZE_BLOCK_SIZE = 2048;
size_t gr_add_to_immediate_buffer(size_t size, void* data) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return 0;
}
@@ -1262,7 +1263,7 @@ size_t gr_add_to_immediate_buffer(size_t size, void* data) {
return old_offset;
}
void gr_reset_immediate_buffer() {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1284,7 +1285,7 @@ void gr_render_primitives_immediate(material* material_info,
int n_verts,
void* data,
size_t size) {
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1300,7 +1301,7 @@ void gr_render_primitives_2d_immediate(material* material_info,
void* data,
size_t size)
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1457,7 +1458,7 @@ static void draw_bitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode,
void gr_bitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode, float angle)
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
@@ -1472,7 +1473,7 @@ void gr_bitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode, float ang
void gr_aabitmap_list(bitmap_rect_list* list, int n_bm, int resize_mode, float angle)
{
if (gr_screen.mode == GR_STUB) {
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
+229
View File
@@ -0,0 +1,229 @@
#include "graphics/shader_preprocess.h"
#include "cfile/cfile.h"
#include "def_files/def_files.h"
#include "graphics/2d.h"
#include "mod_table/mod_table.h"
static void handle_includes_impl(SCP_vector<SCP_string>& include_stack,
SCP_stringstream& output,
int& include_counter,
const SCP_string& filename,
const SCP_string& original)
{
include_stack.emplace_back(filename);
auto current_source_number = include_counter + 1;
const char* INCLUDE_STRING = "#include";
const char* CONDITIONAL_INCLUDE_STRING = "#conditional_include";
SCP_stringstream input(original);
int line_num = 1;
for (SCP_string line; std::getline(input, line);) {
auto include_start = line.find(CONDITIONAL_INCLUDE_STRING);
if (include_start != SCP_string::npos) {
include_start += strlen(CONDITIONAL_INCLUDE_STRING) + 1;
bool require_capability = true;
switch (line.at(include_start)) {
case '+':
require_capability = true;
break;
case '-':
require_capability = false;
break;
default:
Error(LOCATION,
"Shader %s:%d: Malformed conditional_include line. Expected + or -, got %c.",
filename.c_str(), line_num, line.at(include_start));
break;
}
auto first_quote = line.find('"', include_start);
auto second_quote = line.find('"', first_quote + 1);
if (first_quote == SCP_string::npos || second_quote == SCP_string::npos) {
Error(LOCATION,
"Shader %s:%d: Malformed conditional_include line. Could not find both quote characters for capability.",
filename.c_str(), line_num);
}
auto condition = line.substr(first_quote + 1, second_quote - first_quote - 1);
auto capability = std::find_if(&gr_capabilities[0], &gr_capabilities[gr_capabilities_num],
[condition](const gr_capability_def& ext_pair) {
return !stricmp(ext_pair.parse_name, condition.c_str());
});
if (capability == &gr_capabilities[gr_capabilities_num]) {
Error(LOCATION,
"Shader %s:%d: Malformed conditional_include line. Capability %s does not exist.",
filename.c_str(), line_num, condition.c_str());
}
if (gr_is_capable(capability->capability) == require_capability)
include_start = second_quote + 1 - strlen(INCLUDE_STRING);
else
include_start = SCP_string::npos - 1;
} else {
include_start = line.find(INCLUDE_STRING);
}
if (include_start != SCP_string::npos && include_start != SCP_string::npos - 1) {
auto first_quote = line.find('"', include_start + strlen(INCLUDE_STRING));
auto second_quote = line.find('"', first_quote + 1);
if (first_quote == SCP_string::npos || second_quote == SCP_string::npos) {
Error(LOCATION,
"Shader %s:%d: Malformed include line. Could not find both quote characters.",
filename.c_str(), line_num);
}
auto file_name = line.substr(first_quote + 1, second_quote - first_quote - 1);
auto existing_name =
std::find_if(include_stack.begin(), include_stack.end(),
[&file_name](const SCP_string& str) { return str == file_name; });
if (existing_name != include_stack.end()) {
SCP_stringstream stack_string;
for (auto& name : include_stack) {
stack_string << "\t" << name << "\n";
}
Error(LOCATION,
"Shader %s:%d: Detected cyclic include! Previous includes (top level file first):\n%s",
filename.c_str(), line_num, stack_string.str().c_str());
}
++include_counter;
output << "#line 1 " << include_counter + 1 << "\n";
handle_includes_impl(include_stack, output, include_counter, file_name,
shader_load_source(file_name));
output << "#line " << line_num + 1 << " " << current_source_number << "\n";
} else if (include_start != SCP_string::npos - 1) {
output << line << "\n";
}
++line_num;
}
include_stack.pop_back();
}
SCP_string shader_load_source(const SCP_string& filename)
{
SCP_string content;
// Check external shaders first (modding support)
if (Enable_external_shaders) {
CFILE* cf_shader = cfopen(filename.c_str(), "rt", CF_TYPE_EFFECTS);
if (cf_shader != nullptr) {
int len = cfilelength(cf_shader);
content.resize(len);
cfread(content.data(), len + 1, 1, cf_shader);
cfclose(cf_shader);
return content;
}
}
// Fall back to embedded defaults
auto def_shader = defaults_get_file(filename.c_str());
if (def_shader.data != nullptr && def_shader.size > 0) {
content.assign(reinterpret_cast<const char*>(def_shader.data), def_shader.size);
} else {
mprintf(("shader_load_source: Could not load shader source: %s\n", filename.c_str()));
}
return content;
}
SCP_string shader_preprocess_includes(const SCP_string& filename, const SCP_string& source)
{
SCP_stringstream output;
SCP_vector<SCP_string> include_stack;
auto include_counter = 0;
handle_includes_impl(include_stack, output, include_counter, filename, source);
return output.str();
}
SCP_string shader_preprocess_defines(const SCP_string& filename, const SCP_string& source)
{
SCP_stringstream output;
SCP_unordered_map<SCP_string, SCP_string> defines;
//In any shader, define GLOBAL_FAR_Z
output << "#define GLOBAL_FAR_Z " << std::fixed << std::setprecision(2) << Max_draw_distance << std::defaultfloat << '\n';
const char* PREDEFINE_STRING = "#predefine";
const char* PREREPLACE_STRING = "#prereplace";
SCP_stringstream input(source);
for (SCP_string line; std::getline(input, line);) {
auto predefine_start = line.find(PREDEFINE_STRING);
auto prereplace_start = line.find(PREREPLACE_STRING);
if (predefine_start != SCP_string::npos) {
predefine_start += strlen(PREDEFINE_STRING);
auto token_start = line.find(' ', predefine_start);
auto token_end = line.find(' ', token_start + 1);
if (token_start == SCP_string::npos || token_end == SCP_string::npos) {
Error(LOCATION, "Shader %s: Malformed predefine line. Could not find define token.",
filename.c_str());
}
auto token = line.substr(token_start + 1, token_end - token_start - 1);
auto replaceWith = line.substr(token_end + 1);
auto replaceStrToken = replaceWith.find("%s");
if (replaceStrToken == SCP_string::npos ||
replaceWith.find("%s", replaceStrToken + 1) != SCP_string::npos) {
Error(LOCATION,
"Shader %s: Malformed predefine line. Replacing string must have exactly one %%s.",
filename.c_str());
}
if (defines.find(token) != defines.end()) {
Error(LOCATION,
"Shader %s: Malformed predefine line. Token %s is already defined.",
filename.c_str(), token.c_str());
}
defines.emplace(std::move(token), std::move(replaceWith));
output << "\n"; // Preserve line count
} else if (prereplace_start != SCP_string::npos) {
prereplace_start += strlen(PREREPLACE_STRING);
auto token_start = line.find(' ', prereplace_start);
auto token_end = line.find(' ', token_start + 1);
if (token_start == SCP_string::npos || token_end == SCP_string::npos) {
Error(LOCATION, "Shader %s: Malformed prereplace line. Could not find define token.",
filename.c_str());
}
auto token = line.substr(token_start + 1, token_end - token_start - 1);
auto replaceArg = line.substr(token_end + 1);
auto replaceWithIt = defines.find(token);
if (replaceWithIt == defines.end()) {
Error(LOCATION, "Shader %s: Malformed prereplace line. Could not find token %s.",
filename.c_str(), token.c_str());
}
size_t size = replaceWithIt->second.length() - 1 + replaceArg.size();
std::unique_ptr<char[]> buffer = make_unique<char[]>(size);
snprintf(buffer.get(), size, replaceWithIt->second.c_str(), replaceArg.c_str());
buffer[size - 1] = '\0';
output << buffer.get() << "\n";
} else {
output << line << "\n";
}
}
return output.str();
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "globalincs/pstypes.h"
SCP_string shader_load_source(const SCP_string& filename);
SCP_string shader_preprocess_includes(const SCP_string& filename, const SCP_string& source);
SCP_string shader_preprocess_defines(const SCP_string& filename, const SCP_string& source);
+304
View File
@@ -0,0 +1,304 @@
#include "graphics/shader_types.h"
#include "graphics/shadows.h"
#include "mod_table/mod_table.h"
// Pull in MODEL_SDR_FLAG_* constants for the variant table
#define MODEL_SDR_FLAG_MODE_CPP
#include "def_files/data/effects/model_shader_flags.h"
#undef MODEL_SDR_FLAG_MODE_CPP
#undef SDR_FLAG
// ========== Shared shader type table ==========
// Moved from gropenglshader.cpp — single source of truth for both backends.
// clang-format off
static ShaderTypeInfo SHADER_TYPES[] = {
{ SDR_TYPE_MODEL, "main-v.sdr", "main-f.sdr", "main-g.sdr",
{ VATTRIB_POSITION, VATTRIB_TEXCOORD, VATTRIB_NORMAL, VATTRIB_TANGENT, VATTRIB_MODELID }, "Model Rendering", false },
{ SDR_TYPE_EFFECT_PARTICLE, "effect-v.sdr", "effect-f.sdr", "effect-g.sdr",
{ VATTRIB_POSITION, VATTRIB_TEXCOORD, VATTRIB_RADIUS, VATTRIB_COLOR }, "Particle Effects", false },
{ SDR_TYPE_EFFECT_DISTORTION, "effect-distort-v.sdr", "effect-distort-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD, VATTRIB_RADIUS, VATTRIB_COLOR }, "Distortion Effects", false },
{ SDR_TYPE_POST_PROCESS_MAIN, "post-v.sdr", "post-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Post Processing", false },
{ SDR_TYPE_POST_PROCESS_BLUR, "post-v.sdr", "blur-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Gaussian Blur", false },
{ SDR_TYPE_POST_PROCESS_BLOOM_COMP, "post-v.sdr", "bloom-comp-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Bloom Compositing", false },
{ SDR_TYPE_POST_PROCESS_BRIGHTPASS, "post-v.sdr", "brightpass-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Bloom Brightpass", false },
{ SDR_TYPE_POST_PROCESS_FXAA, "fxaa-v.sdr", "fxaa-f.sdr", nullptr,
{ VATTRIB_POSITION }, "FXAA", false },
{ SDR_TYPE_POST_PROCESS_FXAA_PREPASS, "post-v.sdr", "fxaapre-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "FXAA Prepass", false },
{ SDR_TYPE_POST_PROCESS_LIGHTSHAFTS, "post-v.sdr", "ls-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Lightshafts", false },
{ SDR_TYPE_POST_PROCESS_TONEMAPPING, "post-v.sdr", "tonemapping-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Tonemapping", false },
{ SDR_TYPE_DEFERRED_LIGHTING, "deferred-v.sdr", "deferred-f.sdr", nullptr,
{ VATTRIB_POSITION }, "Deferred Lighting", false },
{ SDR_TYPE_DEFERRED_CLEAR, "deferred-clear-v.sdr", "deferred-clear-f.sdr", nullptr,
{ VATTRIB_POSITION }, "Clear Deferred Lighting Buffer", false },
{ SDR_TYPE_VIDEO_PROCESS, "video-v.sdr", "video-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Video Playback", false },
{ SDR_TYPE_PASSTHROUGH_RENDER, "passthrough-v.sdr", "passthrough-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD, VATTRIB_COLOR }, "Passthrough", false },
{ SDR_TYPE_SHIELD_DECAL, "shield-impact-v.sdr", "shield-impact-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_NORMAL }, "Shield Decals", false },
{ SDR_TYPE_BATCHED_BITMAP, "batched-v.sdr", "batched-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD, VATTRIB_COLOR }, "Batched bitmaps", false },
{ SDR_TYPE_DEFAULT_MATERIAL, "default-material-v.sdr", "default-material-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD, VATTRIB_COLOR }, "Default material", false },
{ SDR_TYPE_NANOVG, "nanovg-v.sdr", "nanovg-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "NanoVG shader", false },
{ SDR_TYPE_DECAL, "decal-v.sdr", "decal-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_MODEL_MATRIX }, "Decal rendering", false },
{ SDR_TYPE_SCENE_FOG, "post-v.sdr", "fog-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Scene fogging", false },
{ SDR_TYPE_VOLUMETRIC_FOG, "post-v.sdr", "volumetric-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Volumetric fogging", false },
{ SDR_TYPE_ROCKET_UI, "rocketui-v.sdr", "rocketui-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_COLOR, VATTRIB_TEXCOORD }, "libRocket UI", false },
{ SDR_TYPE_COPY, "post-v.sdr", "copy-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Texture copy", false },
{ SDR_TYPE_COPY_WORLD, "passthrough-v.sdr", "copy-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Texture copy world space", false },
{ SDR_TYPE_MSAA_RESOLVE, "post-v.sdr", "msaa-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "MSAA resolve shader", false },
{ SDR_TYPE_POST_PROCESS_SMAA_EDGE, "smaa-edge-v.sdr", "smaa-edge-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "SMAA Edge detection", false },
{ SDR_TYPE_POST_PROCESS_SMAA_BLENDING_WEIGHT, "smaa-blend-v.sdr", "smaa-blend-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "SMAA Blending weight calculation", false },
{ SDR_TYPE_POST_PROCESS_SMAA_NEIGHBORHOOD_BLENDING, "smaa-neighbour-v.sdr", "smaa-neighbour-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "SMAA Neighborhood Blending", false },
// Vulkan-only: copies the SMAA neighborhood-blend result back into Scene_ldr
// (OpenGL does the equivalent copy directly via opengl_shader_set_passthrough).
{ SDR_TYPE_POST_PROCESS_SMAA_RESOLVE, "post-v.sdr", "smaa-resolve-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "SMAA Resolve", false },
{ SDR_TYPE_ENVMAP_SPHERE_WARP, "post-v.sdr", "envmap-sphere-warp-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Environment Map Export", false },
{ SDR_TYPE_IRRADIANCE_MAP_GEN, "post-v.sdr", "irrmap-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Irradiance Map Generation", false },
{ SDR_TYPE_SHADOW_MAP_GEN, "shadow_map-v.sdr", "shadow_map-f.sdr", "shadow_map-g.sdr",
{ VATTRIB_POSITION, VATTRIB_TEXCOORD, VATTRIB_NORMAL, VATTRIB_TANGENT, VATTRIB_MODELID }, "Shadow Map Generation", false },
{ SDR_TYPE_GAMMA_BLIT, "post-v.sdr", "gamma-correct-f.sdr", nullptr,
{ VATTRIB_POSITION, VATTRIB_TEXCOORD }, "Gamma correct blit", false },
};
// clang-format on
// ========== Shared shader variant table ==========
// Moved from gropenglshader.cpp — single source of truth for both backends.
// MODEL flags come from model_shader_flags.h; others from 2d.h defines.
static ShaderVariantInfo SHADER_VARIANTS[] = {
#define MODEL_SDR_FLAG_MODE_CPP_ARRAY
#include "def_files/data/effects/model_shader_flags.h"
#undef MODEL_SDR_FLAG_MODE_CPP_ARRAY
{SDR_TYPE_EFFECT_PARTICLE, true, SDR_FLAG_PARTICLE_POINT_GEN, "FLAG_EFFECT_GEOMETRY", {VATTRIB_UVEC}, "Geometry shader point-based particles"},
{SDR_TYPE_DEFERRED_LIGHTING, false, SDR_FLAG_ENV_MAP, "ENV_MAP", {}, "Render ambient light with env and irrmaps"},
{SDR_TYPE_DEFERRED_LIGHTING, false, SDR_FLAG_DEFERRED_RT_SHADOWS, "RT_SHADOWS", {}, "Use raytraced (TLAS ray query) shadows instead of cascaded shadow maps"},
{SDR_TYPE_POST_PROCESS_BLUR, false, SDR_FLAG_BLUR_HORIZONTAL, "PASS_0", {}, "Horizontal blur pass"},
{SDR_TYPE_POST_PROCESS_BLUR, false, SDR_FLAG_BLUR_VERTICAL, "PASS_1", {}, "Vertical blur pass"},
{SDR_TYPE_NANOVG, false, SDR_FLAG_NANOVG_EDGE_AA, "EDGE_AA", {}, "NanoVG edge anti-alias"},
{SDR_TYPE_DECAL, false, SDR_FLAG_DECAL_USE_NORMAL_MAP, "USE_NORMAL_MAP", {}, "Decal use scene normal map"},
{SDR_TYPE_MSAA_RESOLVE, false, SDR_FLAG_MSAA_SAMPLES_4, "SAMPLES_4", {}, "Sets the MSAA resolve shader to 4 samples"},
{SDR_TYPE_MSAA_RESOLVE, false, SDR_FLAG_MSAA_SAMPLES_8, "SAMPLES_8", {}, "Sets the MSAA resolve shader to 8 samples"},
{SDR_TYPE_MSAA_RESOLVE, false, SDR_FLAG_MSAA_SAMPLES_16, "SAMPLES_16", {}, "Sets the MSAA resolve shader to 16 samples"},
{SDR_TYPE_VOLUMETRIC_FOG, false, SDR_FLAG_VOLUMETRICS_DO_EDGE_SMOOTHING, "DO_EDGE_SMOOTHING", {}, "Perform costly edge smoothing lookups"},
{SDR_TYPE_VOLUMETRIC_FOG, false, SDR_FLAG_VOLUMETRICS_NOISE, "NOISE", {}, "Add noise to volumetrics"},
{SDR_TYPE_COPY_WORLD, false, SDR_FLAG_COPY_FROM_ARRAY, "COPY_ARRAY", {}, "Expects to copy from an array texture"},
{SDR_TYPE_COPY, false, SDR_FLAG_COPY_CLAMP01, "CLAMP01", {}, "Clamp copy to [0,1] (HDR edge-detection proxy)"},
{SDR_TYPE_POST_PROCESS_TONEMAPPING, false, SDR_FLAG_TONEMAPPING_LINEAR_OUT, "LINEAR_OUT", {}, "Will make the tonemapper output in linear color space and not in sRGB"},
{SDR_TYPE_GAMMA_BLIT, false, SDR_FLAG_GAMMA_HDR10, "HDR10_OUTPUT", {}, "Encode the gamma-corrected frame for HDR10 (PQ / BT.2020) output instead of SDR"},
{SDR_TYPE_SHADOW_MAP_GEN, true, SDR_FLAG_SHADOW_FALLBACK, "GEOMETRY_FALLBACK", {}, "Use a geometry shader to select the cascade layer instead of writing gl_Layer from the vertex shader"}
};
const ShaderTypeInfo* shader_get_type_info(shader_type type)
{
for (auto & i : SHADER_TYPES) {
if (i.type_id == type) {
return &i;
}
}
return nullptr;
}
const ShaderVariantInfo* shader_get_variant_info(shader_type type, int flag)
{
for (auto & i : SHADER_VARIANTS) {
if (i.type_id == type && i.flag == flag) {
return &i;
}
}
return nullptr;
}
void shader_for_each_active_variant(shader_type type, unsigned int flags, const std::function<void(const ShaderVariantInfo&)>& fn)
{
for (auto & i : SHADER_VARIANTS) {
if (i.type_id == type && (flags & i.flag)) {
fn(i);
}
}
}
SCP_string shader_build_variant_defines(shader_type type, unsigned int flags)
{
SCP_string header;
shader_for_each_active_variant(type, flags, [&](const ShaderVariantInfo& v) {
header += "#define ";
header += v.flag_text;
header += "\n";
});
return header;
}
bool shader_variant_requires_raytracing(shader_type type, unsigned int flags)
{
switch (type) {
case SDR_TYPE_MODEL:
return (flags & MODEL_SDR_FLAG_RT_SHADOWS) != 0;
case SDR_TYPE_DEFERRED_LIGHTING:
return (flags & SDR_FLAG_DEFERRED_RT_SHADOWS) != 0;
default:
return false;
}
}
SCP_string shader_get_fxaa_defines(AntiAliasMode aa_mode, bool gather4_alpha)
{
SCP_string defines;
defines.reserve(256);
defines += "#define FXAA_GLSL_120 0\n";
defines += "#define FXAA_GLSL_130 1\n";
if (gather4_alpha) {
defines += "#define FXAA_GATHER4_ALPHA 1\n";
}
switch (aa_mode) {
case AntiAliasMode::None:
defines += "#define FXAA_QUALITY_PRESET 10\n";
defines += "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/6.0)\n";
defines += "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/12.0)\n";
defines += "#define FXAA_QUALITY_SUBPIX 0.33\n";
break;
case AntiAliasMode::FXAA_Low:
defines += "#define FXAA_QUALITY_PRESET 12\n";
defines += "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/8.0)\n";
defines += "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/16.0)\n";
defines += "#define FXAA_QUALITY_SUBPIX 0.33\n";
break;
case AntiAliasMode::FXAA_Medium:
defines += "#define FXAA_QUALITY_PRESET 26\n";
defines += "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/12.0)\n";
defines += "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/24.0)\n";
defines += "#define FXAA_QUALITY_SUBPIX 0.33\n";
break;
case AntiAliasMode::FXAA_High:
defines += "#define FXAA_QUALITY_PRESET 39\n";
defines += "#define FXAA_QUALITY_EDGE_THRESHOLD (1.0/15.0)\n";
defines += "#define FXAA_QUALITY_EDGE_THRESHOLD_MIN (1.0/32.0)\n";
defines += "#define FXAA_QUALITY_SUBPIX 0.33\n";
break;
default:
UNREACHABLE("Unhandled FXAA mode!");
}
return defines;
}
SCP_string shader_get_smaa_defines(AntiAliasMode aa_mode, bool glsl4)
{
SCP_string defines;
defines.reserve(64);
defines += glsl4 ? "#define SMAA_GLSL_4\n" : "#define SMAA_GLSL_3\n";
switch (aa_mode) {
case AntiAliasMode::SMAA_Low:
defines += "#define SMAA_PRESET_LOW\n";
break;
case AntiAliasMode::SMAA_Medium:
defines += "#define SMAA_PRESET_MEDIUM\n";
break;
case AntiAliasMode::SMAA_High:
defines += "#define SMAA_PRESET_HIGH\n";
break;
case AntiAliasMode::SMAA_Ultra:
defines += "#define SMAA_PRESET_ULTRA\n";
break;
default:
UNREACHABLE("Unhandled SMAA mode!");
}
return defines;
}
SCP_string shader_get_shadow_cascade_defines()
{
SCP_string header;
header += "#define NUM_SHADOW_CASCADES ";
header += std::to_string(Num_shadow_cascades + Num_cockpit_shadow_cascades);
header += "\n";
return header;
}
SCP_string shader_get_rt_shadow_light_limit_define()
{
SCP_string header;
header += "#define MAX_RT_SHADOW_LIGHTS ";
header += std::to_string(Max_rt_shadow_lights);
header += "\n";
return header;
}
+89
View File
@@ -0,0 +1,89 @@
#pragma once
#include "globalincs/pstypes.h"
#include "graphics/2d.h"
// Shared vertex attribute locations.
// Both backends (OpenGL and Vulkan) use these same location numbers
// for vertex shader inputs.
enum VertexAttributeLocation : uint32_t {
VATTRIB_POSITION = 0, // vec2, vec3, or vec4
VATTRIB_COLOR = 1, // vec3/vec4 (normalized u8 or float)
VATTRIB_TEXCOORD = 2, // vec2 or vec4
VATTRIB_NORMAL = 3, // vec3
VATTRIB_TANGENT = 4, // vec4
VATTRIB_MODELID = 5, // float
VATTRIB_RADIUS = 6, // float
VATTRIB_UVEC = 7, // vec3
VATTRIB_MODEL_MATRIX = 8, // Occupies locations 8-11
NUM_VERTEX_ATTRIBS = 9,
};
// Shared shader type info — matches the original opengl_shader_type_t layout.
struct ShaderTypeInfo {
shader_type type_id;
const char *vert;
const char *frag;
const char *geo;
SCP_vector<VertexAttributeLocation> attributes;
const char* description;
bool spirv_shader;
};
// Shared shader variant info — matches the original opengl_shader_variant_t layout.
struct ShaderVariantInfo {
shader_type type_id;
bool use_geometry_sdr;
int flag;
SCP_string flag_text;
SCP_vector<VertexAttributeLocation> attributes;
const char* description;
};
// Lookup helper: find type info by shader_type. Returns nullptr if not found.
const ShaderTypeInfo* shader_get_type_info(shader_type type);
// Lookup helper: find variant info by (type, flag). Returns nullptr if not found.
const ShaderVariantInfo* shader_get_variant_info(shader_type type, int flag);
// Iterate all variants matching type whose flag bit is set in flags.
void shader_for_each_active_variant(shader_type type, unsigned int flags, const std::function<void(const ShaderVariantInfo&)>& fn);
// Build the variant #define header for a shader given type+flags.
SCP_string shader_build_variant_defines(shader_type type, unsigned int flags);
// Returns FXAA preprocessor defines for the given AA mode.
// gather4_alpha: whether the backend supports textureGather.
SCP_string shader_get_fxaa_defines(AntiAliasMode aa_mode, bool gather4_alpha);
// Returns SMAA preprocessor defines (shading language + quality preset) for the given AA mode.
// glsl4: whether to target the SMAA_GLSL_4 codepath (textureGather-based diagonal search).
SCP_string shader_get_smaa_defines(AntiAliasMode aa_mode, bool glsl4);
// Returns the "#define NUM_SHADOW_CASCADES <n>\n" preprocessor define, sized to
// the current total cascade count (Num_shadow_cascades + Num_cockpit_shadow_cascades).
// Needed by any shader that references shadow cascade arrays (model, deferred
// lighting, shadow map generation), for both backends.
SCP_string shader_get_shadow_cascade_defines();
// Returns the "#define MAX_RT_SHADOW_LIGHTS <n>\n" preprocessor define, sized to
// Max_rt_shadow_lights. Needed by the MODEL shader's raytraced-shadow code path
// (main-f.sdr), for both backends -- harmless on OpenGL/non-raytraced builds since
// that code path only compiles in under MODEL_SDR_FLAG_RT_SHADOWS.
SCP_string shader_get_rt_shadow_light_limit_define();
// Whether this (type, flags) combination requires the GL_EXT_ray_query extension,
// and therefore a SPIR-V target-env new enough to support it (see
// VulkanShaderCompiler::compile()'s requiresRaytracing parameter). Centralized here
// since the two shader types that can request it (model materials, deferred
// lighting) each encode the raytraced-shadow bit differently.
bool shader_variant_requires_raytracing(shader_type type, unsigned int flags);
@@ -1,51 +0,0 @@
#version 150
layout(std140) uniform genericData
{
mat4 modelMatrix;
vec4 color;
vec4 clipEquation;
int baseMapIndex;
int alphaTexture;
int noTexturing;
int srgb;
float intensity;
float alphaThreshold;
uint clipEnabled;
} _39;
uniform sampler2DArray baseMap;
in vec4 fragTexCoord;
in vec4 fragColor;
out vec4 fragOut0;
void main()
{
vec4 _48 = texture(baseMap, vec3(fragTexCoord.xy, float(_39.baseMapIndex)));
if (_39.alphaThreshold > _48.w)
{
discard;
}
bool _66 = _39.srgb == 1;
vec3 _146;
if (_66)
{
_146 = pow(_48.xyz, vec3(2.2000000476837158203125));
}
else
{
_146 = _48.xyz;
}
vec4 _148;
if (_66)
{
_148 = vec4(pow(fragColor.xyz, vec3(2.2000000476837158203125)), fragColor.w);
}
else
{
_148 = fragColor;
}
fragOut0 = mix(mix(vec4(_146.x, _146.y, _146.z, _48.w) * _148, vec4(_148.xyz, _146.x * _148.w), vec4(float(_39.alphaTexture))), _148, vec4(float(_39.noTexturing))) * _39.intensity;
}
@@ -1,41 +0,0 @@
#version 150
out float gl_ClipDistance[1];
layout(std140) uniform genericData
{
mat4 modelMatrix;
vec4 color;
vec4 clipEquation;
int baseMapIndex;
int alphaTexture;
int noTexturing;
int srgb;
float intensity;
float alphaThreshold;
uint clipEnabled;
} _22;
layout(std140) uniform matrixData
{
mat4 modelViewMatrix;
mat4 projMatrix;
} _36;
out vec4 fragTexCoord;
in vec4 vertTexCoord;
out vec4 fragColor;
in vec4 vertColor;
in vec4 vertPosition;
void main()
{
fragTexCoord = vertTexCoord;
fragColor = vertColor * _22.color;
gl_Position = (_36.projMatrix * _36.modelViewMatrix) * vertPosition;
if (_22.clipEnabled != 0u)
{
gl_ClipDistance[0] = dot(_22.clipEquation, _22.modelMatrix * vertPosition);
}
}
@@ -4,7 +4,7 @@
#include <cstdint>
#include <array>
struct genericData_default_material_frag {
struct genericData_default_material_f_sdr {
SPIRV_FLOAT_MAT_4x4 modelMatrix;
SPIRV_FLOAT_VEC4 color;
SPIRV_FLOAT_VEC4 clipEquation;
@@ -16,14 +16,14 @@ struct genericData_default_material_frag {
float alphaThreshold;
std::uint32_t clipEnabled;
};
static_assert(sizeof(genericData_default_material_frag) == 124, "Size of struct genericData_default_material_frag does not match what is expected for the uniform block!");
static_assert(offsetof(genericData_default_material_frag, modelMatrix) == 0, "Offset of member modelMatrix does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, color) == 64, "Offset of member color does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, clipEquation) == 80, "Offset of member clipEquation does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, baseMapIndex) == 96, "Offset of member baseMapIndex does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, alphaTexture) == 100, "Offset of member alphaTexture does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, noTexturing) == 104, "Offset of member noTexturing does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, srgb) == 108, "Offset of member srgb does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, intensity) == 112, "Offset of member intensity does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, alphaThreshold) == 116, "Offset of member alphaThreshold does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_frag, clipEnabled) == 120, "Offset of member clipEnabled does not match the uniform buffer offset!");
static_assert(sizeof(genericData_default_material_f_sdr) == 124, "Size of struct genericData_default_material_f_sdr does not match what is expected for the uniform block!");
static_assert(offsetof(genericData_default_material_f_sdr, modelMatrix) == 0, "Offset of member modelMatrix does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, color) == 64, "Offset of member color does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, clipEquation) == 80, "Offset of member clipEquation does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, baseMapIndex) == 96, "Offset of member baseMapIndex does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, alphaTexture) == 100, "Offset of member alphaTexture does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, noTexturing) == 104, "Offset of member noTexturing does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, srgb) == 108, "Offset of member srgb does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, intensity) == 112, "Offset of member intensity does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, alphaThreshold) == 116, "Offset of member alphaThreshold does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_f_sdr, clipEnabled) == 120, "Offset of member clipEnabled does not match the uniform buffer offset!");
@@ -4,7 +4,7 @@
#include <cstdint>
#include <array>
struct genericData_default_material_vert {
struct genericData_default_material_v_sdr {
SPIRV_FLOAT_MAT_4x4 modelMatrix;
SPIRV_FLOAT_VEC4 color;
SPIRV_FLOAT_VEC4 clipEquation;
@@ -16,21 +16,21 @@ struct genericData_default_material_vert {
float alphaThreshold;
std::uint32_t clipEnabled;
};
static_assert(sizeof(genericData_default_material_vert) == 124, "Size of struct genericData_default_material_vert does not match what is expected for the uniform block!");
static_assert(offsetof(genericData_default_material_vert, modelMatrix) == 0, "Offset of member modelMatrix does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, color) == 64, "Offset of member color does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, clipEquation) == 80, "Offset of member clipEquation does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, baseMapIndex) == 96, "Offset of member baseMapIndex does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, alphaTexture) == 100, "Offset of member alphaTexture does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, noTexturing) == 104, "Offset of member noTexturing does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, srgb) == 108, "Offset of member srgb does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, intensity) == 112, "Offset of member intensity does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, alphaThreshold) == 116, "Offset of member alphaThreshold does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_vert, clipEnabled) == 120, "Offset of member clipEnabled does not match the uniform buffer offset!");
struct matrixData_default_material_vert {
static_assert(sizeof(genericData_default_material_v_sdr) == 124, "Size of struct genericData_default_material_v_sdr does not match what is expected for the uniform block!");
static_assert(offsetof(genericData_default_material_v_sdr, modelMatrix) == 0, "Offset of member modelMatrix does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, color) == 64, "Offset of member color does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, clipEquation) == 80, "Offset of member clipEquation does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, baseMapIndex) == 96, "Offset of member baseMapIndex does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, alphaTexture) == 100, "Offset of member alphaTexture does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, noTexturing) == 104, "Offset of member noTexturing does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, srgb) == 108, "Offset of member srgb does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, intensity) == 112, "Offset of member intensity does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, alphaThreshold) == 116, "Offset of member alphaThreshold does not match the uniform buffer offset!");
static_assert(offsetof(genericData_default_material_v_sdr, clipEnabled) == 120, "Offset of member clipEnabled does not match the uniform buffer offset!");
struct matrixData_default_material_v_sdr {
SPIRV_FLOAT_MAT_4x4 modelViewMatrix;
SPIRV_FLOAT_MAT_4x4 projMatrix;
};
static_assert(sizeof(matrixData_default_material_vert) == 128, "Size of struct matrixData_default_material_vert does not match what is expected for the uniform block!");
static_assert(offsetof(matrixData_default_material_vert, modelViewMatrix) == 0, "Offset of member modelViewMatrix does not match the uniform buffer offset!");
static_assert(offsetof(matrixData_default_material_vert, projMatrix) == 64, "Offset of member projMatrix does not match the uniform buffer offset!");
static_assert(sizeof(matrixData_default_material_v_sdr) == 128, "Size of struct matrixData_default_material_v_sdr does not match what is expected for the uniform block!");
static_assert(offsetof(matrixData_default_material_v_sdr, modelViewMatrix) == 0, "Offset of member modelViewMatrix does not match the uniform buffer offset!");
static_assert(offsetof(matrixData_default_material_v_sdr, projMatrix) == 64, "Offset of member projMatrix does not match the uniform buffer offset!");
Binary file not shown.
@@ -1,10 +0,0 @@
#version 150
out vec4 outColor;
in vec3 fragColor;
void main()
{
outColor = vec4(fragColor, 1.0);
}
Binary file not shown.
@@ -1,13 +0,0 @@
#version 150
const vec2 _20[3] = vec2[](vec2(0.0, -0.5), vec2(0.5), vec2(-0.5, 0.5));
const vec3 _29[3] = vec3[](vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
out vec3 fragColor;
void main()
{
gl_Position = vec4(_20[gl_VertexID], 0.0, 1.0);
fragColor = _29[gl_VertexID];
}
Loaded 100 of 228 files, more files were not shown because too many files have changed in this diff. Show more