4687: Add Vulkan backend r=def- a=Jupeyy

This adds a Vulkan 1.0 backend and new features to the graphic settings to select the GPU(if multiple Vulkan GPUs are supported, and a new list for renderers (see screenshot below))

Mutleasy benchmark (CPU bound):
Vulkan multithreaded, single threaded vs OpenGL
(lower graphs are histograms, code by `@Chairn` )

Y = Frametimes in microseconds
lower graphs = amount of frametimes that happened (histogram)
(since the renderer speed differs, you should look at it more like a spread of values rather than the actual values)
![Figure_1](https://user-images.githubusercontent.com/6654924/153448356-941222a3-8bd3-424d-8685-a43389a4f691.png)

Vulkan is especially good in these scenarios, beating OpenGL 3.3 almost 3x with my setup (~600-700 FPS vs. ~1700-1800FPS)


Remaining TODO list:
- [x] compile shaders in cmake ( e.g. `https://gist.github.com/evilactually/a0d191701cb48f157b05be7f74d79396` )
- [ ] needs windows vulkan libraries
- [x] add build instructions (packages)
- [x] get away from coherent memory even for staging buffers (flushing memory just seems to be faster)
- [ ] a lot of testing :P

![screenshot_2022-02-10_17-13-46](https://user-images.githubusercontent.com/6654924/153449066-38d8741b-60c1-4c0c-ba50-57cc07aa2f9d.png)

![screenshot_2022-02-10_17-13-50](https://user-images.githubusercontent.com/6654924/153449075-91ef3b7b-7238-4cad-9a4c-aeb2d784238b.png)


If there are bugs and it's crashing inside the driver the best you can do is to start the client with `dbg_gfx 4` which will (if supported) add Khronos standard validation layers + verbose debugging information + validation layer extensions, e.g. a synchronization validation layer and validation errors reported by the GPU driver directly.
(setting for dbg_gfx (0: none, 1: minimal, 2: affects performance, 3: verbose, 4: all))

edits:
fixes #3547 (probably fixes it, fixed some data races)

## Checklist

- [x] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [x] Tested in combination with possibly related configuration options
- [ ] Written a unit test if it works standalone, system.c especially
- [x] Considered possible null pointers and out of bounds array indexing
- [x] Changed no physics that affect existing maps
- [x] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Jupeyy <jupjopjap@gmail.com>
Co-authored-by: Dennis Felsing <dennis@felsin9.de>
This commit is contained in:
bors[bot] 2022-03-21 14:20:54 +00:00 committed by GitHub
commit e632859707
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 10292 additions and 1182 deletions

View file

@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest, ubuntu-18.04]
os: [ubuntu-latest, macOS-latest, windows-latest, ubuntu-20.04]
include:
- os: ubuntu-latest
cmake-args: -G Ninja
@ -23,7 +23,7 @@ jobs:
env:
CFLAGS: -Wdeclaration-after-statement -Werror
CXXFLAGS: -Werror
- os: ubuntu-18.04
- os: ubuntu-20.04
cmake-path: /usr/bin/
cmake-args: -G Ninja
package-file: "*-linux_x86_64.tar.xz"
@ -57,7 +57,7 @@ jobs:
if: contains(matrix.os, 'ubuntu')
run: |
sudo apt-get update -y
sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev -y
sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libvulkan-dev glslang-tools spirv-tools libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev -y
- name: Prepare Linux (fancy)
if: contains(matrix.os, 'ubuntu') && matrix.fancy
@ -78,7 +78,7 @@ jobs:
if: contains(matrix.os, 'macOS')
run: |
brew update || true
brew install pkg-config sdl2 ffmpeg python3 ninja
brew install pkg-config sdl2 ffmpeg python3 ninja molten-vk vulkan-headers glslang spirv-tools
brew upgrade freetype
pip3 install dmgbuild
sudo rm -rf /Library/Developer/CommandLineTools

View file

@ -20,7 +20,7 @@ jobs:
- name: Install clang-tidy
run: |
sudo apt-get update -y
sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev clang-tidy -y
sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev clang-tidy libvulkan-dev glslang-tools spirv-tools -y
- name: Build with clang-tidy
run: |
mkdir clang-tidy

View file

@ -38,7 +38,7 @@ jobs:
if: matrix.language == 'cpp'
run: |
sudo apt-get update -y
sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev libmariadbclient-dev libwebsockets-dev -y
sudo apt-get install pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev libmariadbclient-dev libwebsockets-dev libvulkan-dev glslang-tools spirv-tools -y
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View file

@ -19,7 +19,7 @@ jobs:
- name: Prepare
run: |
sudo apt-get update -y
sudo apt-get install clang-format imagemagick ddnet-tools shellcheck pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev pylint3 python3-clang -y
sudo apt-get install clang-format imagemagick ddnet-tools shellcheck pkg-config cmake ninja-build libfreetype6-dev libnotify-dev libsdl2-dev libsqlite3-dev libavcodec-dev libavformat-dev libavutil-dev libswresample-dev libswscale-dev libx264-dev pylint3 python3-clang libvulkan-dev glslang-tools spirv-tools -y
mkdir release
cd release
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DDOWNLOAD_GTEST=OFF -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE=. ..

View file

@ -93,9 +93,14 @@ else()
endif()
set(AUTO_DEPENDENCIES_DEFAULT OFF)
set(AUTO_VULKAN_BACKEND ON)
if(TARGET_OS STREQUAL "windows")
set(AUTO_DEPENDENCIES_DEFAULT ON)
elseif(TARGET_OS STREQUAL "linux")
if(TARGET_CPU_ARCHITECTURE STREQUAL "x86")
set(AUTO_VULKAN_BACKEND OFF)
endif()
elseif(TARGET_OS STREQUAL "mac")
set(AUTO_VULKAN_BACKEND OFF)
endif()
option(WEBSOCKETS "Enable websockets support" OFF)
@ -116,6 +121,7 @@ option(DISCORD "Enable Discord rich presence support" OFF)
option(DISCORD_DYNAMIC "Enable discovering Discord rich presence libraries at runtime (Linux only)" OFF)
option(PREFER_BUNDLED_LIBS "Prefer bundled libraries over system libraries" ${AUTO_DEPENDENCIES_DEFAULT})
option(DEV "Don't generate stuff necessary for packaging" OFF)
option(VULKAN "Enable the vulkan backend" ${AUTO_VULKAN_BACKEND})
option(EXCEPTION_HANDLING "Enable exception handling (only works with Windows as of now)" OFF)
@ -467,6 +473,15 @@ if(TARGET_OS AND TARGET_OS STREQUAL "mac")
find_program(DMGBUILD dmgbuild)
endif()
set(VULKAN_SHADER_FILE_LIST "" CACHE STRING "Vulkan shader file list")
if(VULKAN)
find_package(Vulkan)
include(cmake/BuildVulkanShaders.cmake)
else()
set(VULKAN_LIBRARIES)
set(VULKAN_INCLUDE_DIRS)
endif()
message(STATUS "******** ${CMAKE_PROJECT_NAME} ********")
set(TARGET "Target OS: ${TARGET_OS} ${CMAKE_SYSTEM_PROCESSOR}")
if(TARGET_OS STREQUAL "mac")
@ -528,6 +543,10 @@ if(WEBSOCKETS)
show_dependency_status("Websockets" WEBSOCKETS)
endif()
if(VULKAN)
show_dependency_status("Vulkan" VULKAN)
endif()
if(CLIENT AND NOT(CURL_FOUND))
message(SEND_ERROR "You must install Curl to compile DDNet")
endif()
@ -591,6 +610,10 @@ if(NOT(GTEST_FOUND))
endif()
endif()
if(VULKAN AND CLIENT AND NOT(VULKAN_FOUND))
message(SEND_ERROR "You must install Vulkan libraries to compile the DDNet client")
endif()
if(TARGET_OS STREQUAL "windows")
set(PLATFORM_CLIENT)
set(PLATFORM_CLIENT_LIBS opengl32 winmm)
@ -1277,6 +1300,20 @@ set(EXPECTED_DATA
shader/text.vert
shader/tile.frag
shader/tile.vert
shader/vulkan/prim.frag
shader/vulkan/prim.vert
shader/vulkan/prim3d.frag
shader/vulkan/prim3d.vert
shader/vulkan/primex.frag
shader/vulkan/primex.vert
shader/vulkan/quad.frag
shader/vulkan/quad.vert
shader/vulkan/spritemulti.frag
shader/vulkan/spritemulti.vert
shader/vulkan/text.frag
shader/vulkan/text.vert
shader/vulkan/tile.frag
shader/vulkan/tile.vert
skins/Aoe4leg.png
skins/PaladiN.png
skins/antiantey.png
@ -1422,6 +1459,7 @@ set(COPY_FILES
${FFMPEG_COPY_FILES}
${WEBSOCKETS_COPY_FILES}
${DISCORDSDK_COPY_FILES}
${VULKAN_COPY_FILES}
${EXCEPTION_HANDLING_COPY_FILES}
)
file(COPY ${COPY_FILES} DESTINATION .)
@ -1756,6 +1794,8 @@ if(CLIENT)
list(APPEND TARGETS_OWN ${TARGET_STEAMAPI})
set_src(ENGINE_CLIENT GLOB_RECURSE src/engine/client
backend/backend_base.cpp
backend/backend_base.h
backend/glsl_shader_compiler.cpp
backend/glsl_shader_compiler.h
backend/opengl/backend_opengl.cpp
@ -1773,6 +1813,8 @@ if(CLIENT)
backend/opengles/gles_class_defines.h
backend/opengles/opengles_sl.cpp
backend/opengles/opengles_sl_program.cpp
backend/vulkan/backend_vulkan.cpp
backend/vulkan/backend_vulkan.h
backend_sdl.cpp
backend_sdl.h
blocklist_driver.cpp
@ -1960,6 +2002,8 @@ if(CLIENT)
${OPUS_LIBRARIES}
${OGG_LIBRARIES}
${VULKAN_LIBRARIES}
${TARGET_STEAMAPI}
${PLATFORM_CLIENT_LIBS}
@ -2031,6 +2075,8 @@ if(CLIENT)
${FFMPEG_INCLUDE_DIRS}
${DISCORDSDK_INCLUDE_DIRS}
${VULKAN_INCLUDE_DIRS}
${PLATFORM_CLIENT_INCLUDE_DIRS}
)
@ -2063,6 +2109,10 @@ if(CLIENT)
target_compile_definitions(${TARGET_CLIENT} PRIVATE CONF_GLEW_HAS_CONTEXT_INIT)
endif()
if(VULKAN)
target_compile_definitions(${TARGET_CLIENT} PRIVATE CONF_BACKEND_VULKAN)
endif()
list(APPEND TARGETS_OWN ${TARGET_CLIENT})
list(APPEND TARGETS_LINK ${TARGET_CLIENT})
endif()
@ -2576,6 +2626,11 @@ set(CPACK_FILES
storage.cfg
${COPY_FILES}
)
set(CPACK_GEN_FILES
${VULKAN_SHADER_FILE_LIST}
)
if(TARGET_OS STREQUAL "windows")
list(APPEND CPACK_FILES other/config_directory.bat)
endif()
@ -2598,6 +2653,9 @@ if(NOT DEV)
install(FILES other/icons/DDNet_${SIZE}x${SIZE}x32.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}x${SIZE}/apps RENAME ddnet.png COMPONENT client)
install(FILES other/icons/DDNet-Server_${SIZE}x${SIZE}x32.png DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${SIZE}x${SIZE}/apps RENAME ddnet-server.png COMPONENT server)
endforeach()
foreach(file ${VULKAN_SHADER_FILE_LIST})
install(FILES ${PROJECT_BINARY_DIR}/${file} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/ddnet/data/shader/vulkan COMPONENT client)
endforeach()
endif()
if(DEV)
@ -2608,7 +2666,8 @@ else()
set(EXTRA_ARGS DESTINATION ${CPACK_PACKAGE_FILE_NAME} COMPONENT portable EXCLUDE_FROM_ALL)
install(TARGETS ${CPACK_TARGETS} ${EXTRA_ARGS})
install(DIRECTORY ${CPACK_DIRS} ${EXTRA_ARGS})
install(FILES ${CPACK_FILES} ${EXTRA_ARGS})
set(CPACK_FILES_TMP ${CPACK_FILES} ${CPACK_GEN_FILES})
install(FILES ${CPACK_FILES_TMP} ${EXTRA_ARGS})
endif()
set(PACKAGE_TARGETS)
@ -2712,6 +2771,9 @@ foreach(ext zip tar.gz tar.xz)
foreach(file ${CPACK_FILES})
list(APPEND COPY_FILE_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/${file} ${TMPDIR}/)
endforeach()
foreach(file ${CPACK_GEN_FILES})
list(APPEND COPY_FILE_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/${file} ${TMPDIR}/${file})
endforeach()
foreach(dir ${CPACK_DIRS})
list(APPEND COPY_DIR_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/${dir} ${TMPDIR}/${dir})
endforeach()
@ -2759,6 +2821,7 @@ unset(CPACK_SOURCE_FILES_INVERTED)
unset(CPACK_TARGETS)
unset(CPACK_DIRS)
unset(CPACK_FILES)
unset(CPACK_GEN_FILES)
include(CPack)

View file

@ -34,21 +34,21 @@ Dependencies on Linux / macOS
You can install the required libraries on your system, `touch CMakeLists.txt` and CMake will use the system-wide libraries by default. You can install all required dependencies and CMake on Debian or Ubuntu like this:
sudo apt install build-essential cmake git google-mock libcurl4-openssl-dev libssl-dev libfreetype6-dev libglew-dev libnotify-dev libogg-dev libopus-dev libopusfile-dev libpnglite-dev libsdl2-dev libsqlite3-dev libwavpack-dev python libx264-dev libavfilter-dev libavdevice-dev libavformat-dev libavcodec-extra libavutil-dev
sudo apt install build-essential cmake git google-mock libcurl4-openssl-dev libssl-dev libfreetype6-dev libglew-dev libnotify-dev libogg-dev libopus-dev libopusfile-dev libpnglite-dev libsdl2-dev libsqlite3-dev libwavpack-dev python libx264-dev libavfilter-dev libavdevice-dev libavformat-dev libavcodec-extra libavutil-dev libvulkan-dev glslang-tools spirv-tools
Or on CentOS, RedHat and AlmaLinux like this:
sudo yum install gcc gcc-c++ make cmake git python2 gtest-devel gmock-devel libcurl-devel openssl-devel freetype-devel glew-devel libnotify-devel libogg-devel opus-devel opusfile-devel pnglite-devel SDL2-devel sqlite-devel wavpack-devel libx264-devel ffmpeg-devel
sudo yum install gcc gcc-c++ make cmake git python2 gtest-devel gmock-devel libcurl-devel openssl-devel freetype-devel glew-devel libnotify-devel libogg-devel opus-devel opusfile-devel pnglite-devel SDL2-devel sqlite-devel wavpack-devel libx264-devel ffmpeg-devel vulkan-devel glslang spirv-tools
Or on Arch Linux like this:
sudo pacman -S --needed base-devel cmake curl freetype2 git glew gmock libnotify opusfile python sdl2 sqlite wavpack x264 ffmpeg
sudo pacman -S --needed base-devel cmake curl freetype2 git glew gmock libnotify opusfile python sdl2 sqlite wavpack x264 ffmpeg vulkan-icd-loader vulkan-headers glslang spirv-tools
There is an [AUR package for pnglite](https://aur.archlinux.org/packages/pnglite/). For instructions on installing it, see [AUR packages installation instructions on ArchWiki](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages).
On macOS you can use [homebrew](https://brew.sh/) to install build dependencies like this:
brew install cmake freetype glew googletest opusfile SDL2 wavpack x264 ffmpeg
brew install cmake freetype glew googletest opusfile SDL2 wavpack x264 ffmpeg molten-vk vulkan-headers glslang spirv-tools
If you don't want to use the system libraries, you can pass the `-DPREFER_BUNDLED_LIBS=ON` parameter to cmake.
@ -109,6 +109,10 @@ Whether to optimize for development, speeding up the compilation process a littl
Whether to enable UPnP support for the server.
You need to install `libminiupnpc-dev` on Debian, `miniupnpc` on Arch Linux.
* **-DVULKAN=[ON|OFF]** <br>
Whether to enable the vulkan backend.
On Windows you need to install the Vulkan SDK and set the `VULKAN_SDK` environment flag accordingly.
* **-GNinja** <br>
Use the Ninja build system instead of Make. This automatically parallizes the build and is generally faster. Compile with `ninja` instead of `make`. Install Ninja with `sudo apt install ninja-build` on Debian, `sudo pacman -S --needed ninja` on Arch Linux.

View file

@ -1,6 +1,6 @@
status = [
"build-cmake (ubuntu-latest)",
"build-cmake (ubuntu-18.04)",
"build-cmake (ubuntu-20.04)",
"build-cmake (macOS-latest)",
"build-cmake (windows-latest)",
"check-clang-tidy",

View file

@ -0,0 +1,175 @@
find_program(GLSLANG_VALIDATOR_PROGRAM glslangValidator)
find_program(SPIRV_OPTIMIZER_PROGRAM spirv-opt)
set(GLSLANG_VALIDATOR_PROGRAM_FOUND TRUE)
if(NOT GLSLANG_VALIDATOR_PROGRAM)
set(GLSLANG_VALIDATOR_PROGRAM_FOUND FALSE)
if(TARGET_OS STREQUAL "windows")
if(${TARGET_CPU_ARCHITECTURE} STREQUAL "x86_64")
set(GLSLANG_VALIDATOR_PROGRAM "$ENV{VULKAN_SDK}/Bin/glslangValidator.exe")
else()
set(GLSLANG_VALIDATOR_PROGRAM "$ENV{VULKAN_SDK}/Bin32/glslangValidator.exe")
endif()
endif()
if(EXISTS ${GLSLANG_VALIDATOR_PROGRAM})
set(GLSLANG_VALIDATOR_PROGRAM_FOUND TRUE)
elseif(${TARGET_CPU_ARCHITECTURE} STREQUAL "x86_64")
set(GLSLANG_VALIDATOR_PROGRAM "${PROJECT_SOURCE_DIR}/ddnet-libs/vulkan/windows/lib64/glslangValidator.exe")
if(EXISTS ${GLSLANG_VALIDATOR_PROGRAM})
set(GLSLANG_VALIDATOR_PROGRAM_FOUND TRUE)
endif()
endif()
if(${GLSLANG_VALIDATOR_PROGRAM_FOUND} EQUAL FALSE)
message(FATAL_ERROR "glslangValidator binary was not found. Did you install the Vulkan SDK / packages ?")
endif()
endif()
set(SPIRV_OPTIMIZER_PROGRAM_FOUND TRUE)
if(NOT SPIRV_OPTIMIZER_PROGRAM)
set(SPIRV_OPTIMIZER_PROGRAM_FOUND FALSE)
if(TARGET_OS STREQUAL "windows")
if (${TARGET_CPU_ARCHITECTURE} STREQUAL "x86_64")
set(SPIRV_OPTIMIZER_PROGRAM "$ENV{VULKAN_SDK}/Bin/spirv-opt.exe")
else()
set(SPIRV_OPTIMIZER_PROGRAM "$ENV{VULKAN_SDK}/Bin32/spirv-opt.exe")
endif()
endif()
if(EXISTS ${SPIRV_OPTIMIZER_PROGRAM})
set(SPIRV_OPTIMIZER_PROGRAM_FOUND TRUE)
endif()
endif()
file(GLOB_RECURSE GLSL_SHADER_FILES
"data/shaders/vulkan/*.frag"
"data/shaders/vulkan/*.vert"
)
set(TMP_SHADER_SHA256_LIST "")
foreach(GLSL_SHADER_FILE ${GLSL_SHADER_FILES})
file(SHA256 ${FILE_NAME} TMP_FILE_SHA)
set(TMP_SHADER_SHA256_LIST "${TMP_SHADER_SHA256_LIST}${TMP_FILE_SHA}")
endforeach(GLSL_SHADER_FILE)
string(SHA256 GLSL_SHADER_SHA256 "${TMP_SHADER_SHA256_LIST}")
set(GLSL_SHADER_SHA256 "${GLSL_SHADER_SHA256}@v1")
set(FOUND_MATCHING_SHA256_FILE FALSE)
if(EXISTS "${PROJECT_BINARY_DIR}/vulkan_shaders_sha256.txt")
file(STRINGS "${PROJECT_BINARY_DIR}/vulkan_shaders_sha256.txt" VULKAN_SHADERS_SHA256_FILE_CONTENT)
if("${VULKAN_SHADERS_SHA256_FILE_CONTENT}" STREQUAL "${GLSL_SHADER_SHA256}")
set(FOUND_MATCHING_SHA256_FILE TRUE)
endif()
endif()
set(TW_VULKAN_VERSION "vulkan100")
set(GLSLANG_VALIDATOR_COMMAND_LIST)
set(GLSLANG_VALIDATOR_DELETE_LIST)
set(SPIRV_OPTIMIZER_COMMAND_LIST)
function(generate_shader_file FILE_ARGS1 FILE_ARGS2 FILE_NAME FILE_OUTPUT_NAME)
set(FILE_TMP_NAME_POSTFIX "")
if(SPIRV_OPTIMIZER_PROGRAM_FOUND)
set(FILE_TMP_NAME_POSTFIX ".tmp")
endif()
list(APPEND GLSLANG_VALIDATOR_COMMAND_LIST COMMAND ${GLSLANG_VALIDATOR_PROGRAM} --client ${TW_VULKAN_VERSION} ${FILE_ARGS1} ${FILE_ARGS2} ${FILE_NAME} -o "${PROJECT_BINARY_DIR}/${FILE_OUTPUT_NAME}${FILE_TMP_NAME_POSTFIX}")
if(SPIRV_OPTIMIZER_PROGRAM_FOUND)
list(APPEND SPIRV_OPTIMIZER_COMMAND_LIST COMMAND ${SPIRV_OPTIMIZER_PROGRAM} -O "${PROJECT_BINARY_DIR}/${FILE_OUTPUT_NAME}${FILE_TMP_NAME_POSTFIX}" -o "${PROJECT_BINARY_DIR}/${FILE_OUTPUT_NAME}")
list(APPEND GLSLANG_VALIDATOR_DELETE_LIST "${PROJECT_BINARY_DIR}/${FILE_OUTPUT_NAME}${FILE_TMP_NAME_POSTFIX}")
endif()
file(RELATIVE_PATH TMP_SHADER_FILE_REL "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/${FILE_OUTPUT_NAME}")
list(APPEND VULKAN_SHADER_FILE_LIST "${FILE_OUTPUT_NAME}")
set(VULKAN_SHADER_FILE_LIST ${VULKAN_SHADER_FILE_LIST} PARENT_SCOPE)
set(GLSLANG_VALIDATOR_DELETE_LIST ${GLSLANG_VALIDATOR_DELETE_LIST} PARENT_SCOPE)
set(SPIRV_OPTIMIZER_COMMAND_LIST ${SPIRV_OPTIMIZER_COMMAND_LIST} PARENT_SCOPE)
set(GLSLANG_VALIDATOR_COMMAND_LIST ${GLSLANG_VALIDATOR_COMMAND_LIST} PARENT_SCOPE)
endfunction()
if(NOT FOUND_MATCHING_SHA256_FILE)
message(STATUS "Building vulkan shaders")
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/data/shader/vulkan/")
unset(VULKAN_SHADER_FILE_LIST CACHE)
# primitives
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/prim.frag" "data/shader/vulkan/prim.frag.spv")
generate_shader_file("-DTW_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/prim.frag" "data/shader/vulkan/prim_textured.frag.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/prim.vert" "data/shader/vulkan/prim.vert.spv")
generate_shader_file("-DTW_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/prim.vert" "data/shader/vulkan/prim_textured.vert.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/prim3d.frag" "data/shader/vulkan/prim3d.frag.spv")
generate_shader_file("-DTW_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/prim3d.frag" "data/shader/vulkan/prim3d_textured.frag.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/prim3d.vert" "data/shader/vulkan/prim3d.vert.spv")
generate_shader_file("-DTW_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/prim3d.vert" "data/shader/vulkan/prim3d_textured.vert.spv")
# text
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/text.frag" "data/shader/vulkan/text.frag.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/text.vert" "data/shader/vulkan/text.vert.spv")
# quad container
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/primex.frag" "data/shader/vulkan/primex.frag.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/primex.vert" "data/shader/vulkan/primex.vert.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/primex.frag" "data/shader/vulkan/primex_rotationless.frag.spv")
generate_shader_file("-DTW_ROTATIONLESS" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/primex.vert" "data/shader/vulkan/primex_rotationless.vert.spv")
generate_shader_file("-DTW_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/primex.frag" "data/shader/vulkan/primex_tex.frag.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/primex.vert" "data/shader/vulkan/primex_tex.vert.spv")
generate_shader_file("-DTW_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/primex.frag" "data/shader/vulkan/primex_tex_rotationless.frag.spv")
generate_shader_file("-DTW_ROTATIONLESS" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/primex.vert" "data/shader/vulkan/primex_tex_rotationless.vert.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/spritemulti.frag" "data/shader/vulkan/spritemulti.frag.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/spritemulti.vert" "data/shader/vulkan/spritemulti.vert.spv")
generate_shader_file("-DTW_PUSH_CONST" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/spritemulti.frag" "data/shader/vulkan/spritemulti_push.frag.spv")
generate_shader_file("-DTW_PUSH_CONST" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/spritemulti.vert" "data/shader/vulkan/spritemulti_push.vert.spv")
# tile layer
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.frag" "data/shader/vulkan/tile.frag.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.vert" "data/shader/vulkan/tile.vert.spv")
generate_shader_file("-DTW_TILE_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.frag" "data/shader/vulkan/tile_textured.frag.spv")
generate_shader_file("-DTW_TILE_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.vert" "data/shader/vulkan/tile_textured.vert.spv")
generate_shader_file("-DTW_TILE_BORDER" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.frag" "data/shader/vulkan/tile_border.frag.spv")
generate_shader_file("-DTW_TILE_BORDER" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.vert" "data/shader/vulkan/tile_border.vert.spv")
generate_shader_file("-DTW_TILE_BORDER" "-DTW_TILE_TEXTURED" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.frag" "data/shader/vulkan/tile_border_textured.frag.spv")
generate_shader_file("-DTW_TILE_BORDER" "-DTW_TILE_TEXTURED" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.vert" "data/shader/vulkan/tile_border_textured.vert.spv")
generate_shader_file("-DTW_TILE_BORDER_LINE" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.frag" "data/shader/vulkan/tile_border_line.frag.spv")
generate_shader_file("-DTW_TILE_BORDER_LINE" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.vert" "data/shader/vulkan/tile_border_line.vert.spv")
generate_shader_file("-DTW_TILE_BORDER_LINE" "-DTW_TILE_TEXTURED" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.frag" "data/shader/vulkan/tile_border_line_textured.frag.spv")
generate_shader_file("-DTW_TILE_BORDER_LINE" "-DTW_TILE_TEXTURED" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/tile.vert" "data/shader/vulkan/tile_border_line_textured.vert.spv")
# quad layer
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/quad.frag" "data/shader/vulkan/quad.frag.spv")
generate_shader_file("" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/quad.vert" "data/shader/vulkan/quad.vert.spv")
generate_shader_file("-DTW_PUSH_CONST" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/quad.frag" "data/shader/vulkan/quad_push.frag.spv")
generate_shader_file("-DTW_PUSH_CONST" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/quad.vert" "data/shader/vulkan/quad_push.vert.spv")
generate_shader_file("-DTW_QUAD_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/quad.frag" "data/shader/vulkan/quad_textured.frag.spv")
generate_shader_file("-DTW_QUAD_TEXTURED" "" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/quad.vert" "data/shader/vulkan/quad_textured.vert.spv")
generate_shader_file("-DTW_QUAD_TEXTURED" "-DTW_PUSH_CONST" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/quad.frag" "data/shader/vulkan/quad_push_textured.frag.spv")
generate_shader_file("-DTW_QUAD_TEXTURED" "-DTW_PUSH_CONST" "${PROJECT_SOURCE_DIR}/data/shader/vulkan/quad.vert" "data/shader/vulkan/quad_push_textured.vert.spv")
execute_process(${GLSLANG_VALIDATOR_COMMAND_LIST})
if(SPIRV_OPTIMIZER_PROGRAM_FOUND)
execute_process(${SPIRV_OPTIMIZER_COMMAND_LIST})
file(REMOVE ${GLSLANG_VALIDATOR_DELETE_LIST})
endif()
set(VULKAN_SHADER_FILE_LIST ${VULKAN_SHADER_FILE_LIST} CACHE STRING "Vulkan shader file list" FORCE)
message(STATUS "Finished building vulkan shaders")
file(WRITE "${PROJECT_BINARY_DIR}/vulkan_shaders_sha256.txt" "${GLSL_SHADER_SHA256}")
endif()

61
cmake/FindVulkan.cmake Normal file
View file

@ -0,0 +1,61 @@
if(NOT CMAKE_CROSSCOMPILING)
find_package(PkgConfig QUIET)
pkg_check_modules(PC_VULKAN vulkan)
if(PC_VULKAN_FOUND)
set(VULKAN_INCLUDE_DIRS "${PC_VULKAN_INCLUDE_DIRS}")
set(VULKAN_LIBRARIES "${PC_VULKAN_LIBRARIES}")
set(VULKAN_FOUND TRUE)
endif()
endif()
if(NOT VULKAN_FOUND)
if(TARGET_OS STREQUAL "android")
find_library(VULKAN_LIBRARIES
NAMES vulkan
)
find_path(
VULKAN_INCLUDE_DIRS
NAMES vulkan/vulkan.h
)
elseif(TARGET_OS STREQUAL "mac")
find_library(VULKAN_LIBRARIES
NAMES MoltenVK
)
find_path(
VULKAN_INCLUDE_DIRS
NAMES vulkan/vulkan.h
)
else()
set_extra_dirs_lib(VULKAN vulkan)
find_library(VULKAN_LIBRARIES
NAMES vulkan vulkan-1
HINTS ${HINTS_VULKAN_LIBDIR} ${PC_VULKAN_LIBDIR} ${PC_VULKAN_LIBRARY_DIRS}
PATHS ${PATHS_VULKAN_LIBDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
set_extra_dirs_include(VULKAN vulkan "${VULKAN_LIBRARIES}")
find_path(
VULKAN_INCLUDE_DIRS
NAMES vulkan/vulkan.h
HINTS ${HINTS_VULKAN_INCLUDEDIR} ${PC_VULKAN_INCLUDEDIR} ${PC_VULKAN_INCLUDE_DIRS}
PATHS ${PATHS_VULKAN_INCLUDEDIR}
${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH}
)
endif()
if(VULKAN_INCLUDE_DIRS AND VULKAN_LIBRARIES)
set(VULKAN_FOUND TRUE)
else(VULKAN_INCLUDE_DIRS AND VULKAN_LIBRARIES)
set(VULKAN_FOUND FALSE)
endif(VULKAN_INCLUDE_DIRS AND VULKAN_LIBRARIES)
endif()
if(TARGET_OS STREQUAL "windows")
is_bundled(VULKAN_BUNDLED "${VULKAN_LIBRARIES}")
if(VULKAN_BUNDLED)
set(VULKAN_COPY_FILES "${EXTRA_VULKAN_LIBDIR}/vulkan-1.dll")
endif()
endif()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 B

After

Width:  |  Height:  |  Size: 114 B

View file

@ -0,0 +1,20 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
#ifdef TW_TEXTURED
layout(binding = 0) uniform sampler2D gTextureSampler;
#endif
layout(location = 0) noperspective in vec2 texCoord;
layout(location = 1) noperspective in vec4 vertColor;
layout(location = 0) out vec4 FragClr;
void main()
{
#ifdef TW_TEXTURED
vec4 tex = texture(gTextureSampler, texCoord);
FragClr = tex * vertColor;
#else
FragClr = vertColor;
#endif
}

View file

@ -0,0 +1,20 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec2 inVertexTexCoord;
layout (location = 2) in vec4 inVertexColor;
layout(push_constant) uniform SPosBO {
layout(offset = 0) mat4x2 gPos;
} gPosBO;
layout (location = 0) noperspective out vec2 texCoord;
layout (location = 1) noperspective out vec4 vertColor;
void main()
{
gl_Position = vec4(gPosBO.gPos * vec4(inVertex, 0.0, 1.0), 0.0, 1.0);
texCoord = inVertexTexCoord;
vertColor = vec4(inVertexColor);
}

View file

@ -0,0 +1,24 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
#ifdef TW_TEXTURED
layout (binding = 0) uniform sampler2DArray gTextureSampler;
#endif
layout (location = 0) noperspective in vec4 oVertColor;
#ifdef TW_TEXTURED
layout (location = 1) noperspective in vec3 oTexCoord;
#endif
layout (location = 0) out vec4 FragClr;
void main()
{
#ifdef TW_TEXTURED
vec4 TexColor = texture(gTextureSampler, oTexCoord.xyz).rgba;
FragClr = TexColor.rgba * oVertColor.rgba;
#else
FragClr = oVertColor.rgba;
#endif
}

View file

@ -0,0 +1,24 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec4 inVertexColor;
layout (location = 2) in vec3 inVertexTexCoord;
layout(push_constant) uniform SPosBO {
layout(offset = 0) mat4x2 gPos;
} gPosBO;
layout (location = 0) noperspective out vec4 oVertColor;
#ifdef TW_TEXTURED
layout (location = 1) noperspective out vec3 oTexCoord;
#endif
void main()
{
gl_Position = vec4(gPosBO.gPos * vec4(inVertex, 0.0, 1.0), 0.0, 1.0);
#ifdef TW_TEXTURED
oTexCoord = inVertexTexCoord;
#endif
oVertColor = inVertexColor;
}

View file

@ -0,0 +1,24 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
#ifdef TW_TEXTURED
layout(binding = 0) uniform sampler2D gTextureSampler;
#endif
layout(push_constant) uniform SVertexColorBO {
layout(offset = 48) vec4 gVerticesColor;
} gColorBO;
layout (location = 0) noperspective in vec2 texCoord;
layout (location = 1) noperspective in vec4 vertColor;
layout (location = 0) out vec4 FragClr;
void main()
{
#ifdef TW_TEXTURED
vec4 tex = texture(gTextureSampler, texCoord);
FragClr = tex * vertColor * gColorBO.gVerticesColor;
#else
FragClr = vertColor * gColorBO.gVerticesColor;
#endif
}

View file

@ -0,0 +1,33 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec2 inVertexTexCoord;
layout (location = 2) in vec4 inVertexColor;
layout(push_constant) uniform SPosBO {
layout(offset = 0) mat4x2 gPos;
#ifndef TW_ROTATIONLESS
layout(offset = 32) vec2 gCenter;
layout(offset = 40) float gRotation;
#endif
} gPosBO;
layout (location = 0) noperspective out vec2 texCoord;
layout (location = 1) noperspective out vec4 vertColor;
void main()
{
vec2 FinalPos = vec2(inVertex.xy);
#ifndef TW_ROTATIONLESS
float X = FinalPos.x - gPosBO.gCenter.x;
float Y = FinalPos.y - gPosBO.gCenter.y;
FinalPos.x = X * cos(gPosBO.gRotation) - Y * sin(gPosBO.gRotation) + gPosBO.gCenter.x;
FinalPos.y = X * sin(gPosBO.gRotation) + Y * cos(gPosBO.gRotation) + gPosBO.gCenter.y;
#endif
gl_Position = vec4(gPosBO.gPos * vec4(FinalPos, 0.0, 1.0), 0.0, 1.0);
texCoord = inVertexTexCoord;
vertColor = inVertexColor;
}

View file

@ -0,0 +1,62 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
#ifdef TW_QUAD_TEXTURED
layout (set = 0, binding = 0) uniform sampler2D gTextureSampler;
#endif
#ifdef TW_QUAD_TEXTURED
#define UBOSetIndex 1
#else
#define UBOSetIndex 0
#endif
struct SQuadUniformEl {
vec4 gVertColor;
vec2 gOffset;
float gRotation;
};
#ifndef TW_PUSH_CONST
#define TW_MAX_QUADS 256
layout (std140, set = UBOSetIndex, binding = 1) uniform SOffBO {
uniform SQuadUniformEl gUniEls[TW_MAX_QUADS];
} gQuadBO;
#else
#define gQuadBO gPosBO
#define QuadIndex 0
#endif
layout(push_constant) uniform SPosBO {
layout(offset = 0) uniform mat4x2 gPos;
#ifdef TW_PUSH_CONST
layout(offset = 32) uniform SQuadUniformEl gUniEls[1];
layout(offset = 64) uniform int gQuadOffset;
#else
layout(offset = 32) uniform int gQuadOffset;
#endif
} gPosBO;
layout (location = 0) noperspective in vec4 QuadColor;
#ifndef TW_PUSH_CONST
layout (location = 1) flat in int QuadIndex;
#endif
#ifdef TW_QUAD_TEXTURED
#ifndef TW_PUSH_CONST
layout (location = 2) noperspective in vec2 TexCoord;
#else
layout (location = 1) noperspective in vec2 TexCoord;
#endif
#endif
layout (location = 0) out vec4 FragClr;
void main()
{
#ifdef TW_QUAD_TEXTURED
vec4 TexColor = texture(gTextureSampler, TexCoord);
FragClr = TexColor * QuadColor * gQuadBO.gUniEls[QuadIndex].gVertColor;
#else
FragClr = QuadColor * gQuadBO.gUniEls[QuadIndex].gVertColor;
#endif
}

View file

@ -0,0 +1,83 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec4 inVertex;
layout (location = 1) in vec4 inColor;
#ifdef TW_QUAD_TEXTURED
layout (location = 2) in vec2 inVertexTexCoord;
#endif
#ifdef TW_QUAD_TEXTURED
#define UBOSetIndex 1
#else
#define UBOSetIndex 0
#endif
struct SQuadUniformEl {
vec4 gVertColor;
vec2 gOffset;
float gRotation;
};
#ifndef TW_PUSH_CONST
#define TW_MAX_QUADS 256
layout (std140, set = UBOSetIndex, binding = 1) uniform SOffBO {
uniform SQuadUniformEl gUniEls[TW_MAX_QUADS];
} gQuadBO;
#else
#define gQuadBO gPosBO
#define TmpQuadIndex 0
#endif
layout(push_constant) uniform SPosBO {
layout(offset = 0) uniform mat4x2 gPos;
#ifdef TW_PUSH_CONST
layout(offset = 32) uniform SQuadUniformEl gUniEls[1];
layout(offset = 64) uniform int gQuadOffset;
#else
layout(offset = 32) uniform int gQuadOffset;
#endif
} gPosBO;
layout (location = 0) noperspective out vec4 QuadColor;
#ifndef TW_PUSH_CONST
layout (location = 1) flat out int QuadIndex;
#endif
#ifdef TW_QUAD_TEXTURED
#ifndef TW_PUSH_CONST
layout (location = 2) noperspective out vec2 TexCoord;
#else
layout (location = 1) noperspective out vec2 TexCoord;
#endif
#endif
void main()
{
vec2 FinalPos = vec2(inVertex.xy);
#ifndef TW_PUSH_CONST
int TmpQuadIndex = int(gl_VertexIndex / 4) - gPosBO.gQuadOffset;
#endif
if(gQuadBO.gUniEls[TmpQuadIndex].gRotation != 0.0)
{
float X = FinalPos.x - inVertex.z;
float Y = FinalPos.y - inVertex.w;
FinalPos.x = X * cos(gQuadBO.gUniEls[TmpQuadIndex].gRotation) - Y * sin(gQuadBO.gUniEls[TmpQuadIndex].gRotation) + inVertex.z;
FinalPos.y = X * sin(gQuadBO.gUniEls[TmpQuadIndex].gRotation) + Y * cos(gQuadBO.gUniEls[TmpQuadIndex].gRotation) + inVertex.w;
}
FinalPos.x = FinalPos.x / 1024.0 + gQuadBO.gUniEls[TmpQuadIndex].gOffset.x;
FinalPos.y = FinalPos.y / 1024.0 + gQuadBO.gUniEls[TmpQuadIndex].gOffset.y;
gl_Position = vec4(gPosBO.gPos * vec4(FinalPos, 0.0, 1.0), 0.0, 1.0);
QuadColor = inColor;
#ifndef TW_PUSH_CONST
QuadIndex = TmpQuadIndex;
#endif
#ifdef TW_QUAD_TEXTURED
TexCoord = inVertexTexCoord;
#endif
}

View file

@ -0,0 +1,13 @@
struct SQuadUniformEl {
vec4 gVertColor;
vec2 gOffset;
float gRotation;
};
#define TW_MAX_QUADS 256
layout (std140, set = 2, binding = 2) uniform SOffBO {
uniform SQuadUniformEl gUniEls[TW_MAX_QUADS];
} gQuadBO;

View file

@ -0,0 +1,23 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (set = 0, binding = 0) uniform sampler2D gTextureSampler;
layout(push_constant) uniform SVertexColorBO {
#ifdef TW_PUSH_CONST
layout(offset = 64) vec4 gVerticesColor;
#else
layout(offset = 48) vec4 gVerticesColor;
#endif
} gColorBO;
layout (location = 0) noperspective in vec2 texCoord;
layout (location = 1) noperspective in vec4 vertColor;
layout (location = 0) out vec4 FragClr;
void main()
{
vec4 tex = texture(gTextureSampler, texCoord);
FragClr = tex * vertColor * gColorBO.gVerticesColor;
}

View file

@ -0,0 +1,50 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec2 inVertexTexCoord;
layout (location = 2) in vec4 inVertexColor;
layout(push_constant) uniform SPosBO {
layout(offset = 0) uniform mat4x2 gPos;
layout(offset = 32) uniform vec2 gCenter;
#ifdef TW_PUSH_CONST
layout(offset = 48) uniform vec4 gRSP[1];
#endif
} gPosBO;
#ifndef TW_PUSH_CONST
layout (std140, set = 1, binding = 1) uniform SRSPBO {
vec4 gRSP[512];
} gRSPBO;
#define RSPIndex gl_InstanceIndex
#else
#define gRSPBO gPosBO
#define RSPIndex 0
#endif
layout (location = 0) noperspective out vec2 texCoord;
layout (location = 1) noperspective out vec4 vertColor;
void main()
{
vec2 FinalPos = vec2(inVertex.xy);
if(gRSPBO.gRSP[RSPIndex].w != 0.0)
{
float X = FinalPos.x - gPosBO.gCenter.x;
float Y = FinalPos.y - gPosBO.gCenter.y;
FinalPos.x = X * cos(gRSPBO.gRSP[RSPIndex].w) - Y * sin(gRSPBO.gRSP[RSPIndex].w) + gPosBO.gCenter.x;
FinalPos.y = X * sin(gRSPBO.gRSP[RSPIndex].w) + Y * cos(gRSPBO.gRSP[RSPIndex].w) + gPosBO.gCenter.y;
}
FinalPos.x *= gRSPBO.gRSP[RSPIndex].z;
FinalPos.y *= gRSPBO.gRSP[RSPIndex].z;
FinalPos.x += gRSPBO.gRSP[RSPIndex].x;
FinalPos.y += gRSPBO.gRSP[RSPIndex].y;
gl_Position = vec4(gPosBO.gPos * vec4(FinalPos, 0.0, 1.0), 0.0, 1.0);
texCoord = inVertexTexCoord;
vertColor = inVertexColor;
}

View file

@ -0,0 +1,43 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(binding = 0) uniform sampler2D gTextSampler;
layout(binding = 1) uniform sampler2D gTextOutlineSampler;
layout(push_constant) uniform SFragConstBO {
layout(offset = 48) uniform vec4 gVertColor;
layout(offset = 64) uniform vec4 gVertOutlineColor;
} gFragConst;
layout (location = 0) noperspective in vec2 texCoord;
layout (location = 1) noperspective in vec4 outVertColor;
layout(location = 0) out vec4 FragClr;
void main()
{
vec4 textColor = gFragConst.gVertColor * outVertColor * vec4(1.0, 1.0, 1.0, texture(gTextSampler, texCoord).r);
vec4 textOutlineTex = gFragConst.gVertOutlineColor * vec4(1.0, 1.0, 1.0, texture(gTextOutlineSampler, texCoord).r);
// ratio between the two textures
float OutlineBlend = (1.0 - textColor.a);
// since the outline is always black, or even if it has decent colors, it can be just added to the actual color
// without loosing any or too much color
// lerp isn't commutative, so add the color the fragment looses by lerping
// this reduces the chance of false color calculation if the text is transparent
// first get the right color
vec4 textOutlineFrag = vec4(textOutlineTex.rgb * textOutlineTex.a, textOutlineTex.a) * OutlineBlend;
vec3 textFrag = (textColor.rgb * textColor.a);
vec3 finalFragColor = textOutlineFrag.rgb + textFrag;
float RealAlpha = (textOutlineFrag.a + textColor.a);
// simply add the color we will loose through blending
if(RealAlpha > 0.0)
FragClr = vec4(finalFragColor / RealAlpha, RealAlpha);
else
FragClr = vec4(0.0, 0.0, 0.0, 0.0);
}

View file

@ -0,0 +1,22 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 inVertex;
layout (location = 1) in vec2 inVertexTexCoord;
layout (location = 2) in vec4 inVertexColor;
layout(push_constant) uniform SPosBO {
layout(offset = 0) mat4x2 gPos;
layout(offset = 32) float gTextureSize;
} gPosBO;
layout (location = 0) noperspective out vec2 texCoord;
layout (location = 1) noperspective out vec4 outVertColor;
void main()
{
gl_Position = vec4(gPosBO.gPos * vec4(inVertex, 0.0, 1.0), 0.0, 1.0);
texCoord = vec2(inVertexTexCoord.x / gPosBO.gTextureSize, inVertexTexCoord.y / gPosBO.gTextureSize);
outVertColor = inVertexColor;
}

View file

@ -0,0 +1,25 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
#ifdef TW_TILE_TEXTURED
layout(binding = 0) uniform sampler2DArray gTextureSampler;
#endif
layout(push_constant) uniform SVertexColorBO {
layout(offset = 64) uniform vec4 gVertColor;
} gColorBO;
#ifdef TW_TILE_TEXTURED
layout (location = 0) noperspective in vec3 TexCoord;
#endif
layout (location = 0) out vec4 FragClr;
void main()
{
#ifdef TW_TILE_TEXTURED
vec4 TexColor = texture(gTextureSampler, TexCoord.xyz);
FragClr = TexColor * gColorBO.gVertColor;
#else
FragClr = gColorBO.gVertColor;
#endif
}

View file

@ -0,0 +1,49 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout (location = 0) in vec2 inVertex;
#ifdef TW_TILE_TEXTURED
layout (location = 1) in vec3 inVertexTexCoord;
#endif
layout(push_constant) uniform SPosBO {
layout(offset = 0) uniform mat4x2 gPos;
#if defined(TW_TILE_BORDER) || defined(TW_TILE_BORDER_LINE)
layout(offset = 32) uniform vec2 gDir;
layout(offset = 40) uniform vec2 gOffset;
#endif
#if defined(TW_TILE_BORDER)
layout(offset = 48) uniform int gJumpIndex;
#endif
} gPosBO;
#ifdef TW_TILE_TEXTURED
layout (location = 0) noperspective out vec3 TexCoord;
#endif
void main()
{
#if defined(TW_TILE_BORDER)
vec4 VertPos = vec4(inVertex, 0.0, 1.0);
int XCount = gl_InstanceIndex - (int(gl_InstanceIndex/gPosBO.gJumpIndex) * gPosBO.gJumpIndex);
int YCount = (int(gl_InstanceIndex/gPosBO.gJumpIndex));
VertPos.x += gPosBO.gOffset.x + gPosBO.gDir.x * float(XCount);
VertPos.y += gPosBO.gOffset.y + gPosBO.gDir.y * float(YCount);
gl_Position = vec4(gPosBO.gPos * VertPos, 0.0, 1.0);
#elif defined(TW_TILE_BORDER_LINE)
vec4 VertPos = vec4(inVertex.x + gPosBO.gOffset.x, inVertex.y + gPosBO.gOffset.y, 0.0, 1.0);
VertPos.x += gPosBO.gDir.x * float(gl_InstanceIndex);
VertPos.y += gPosBO.gDir.y * float(gl_InstanceIndex);
gl_Position = vec4(gPosBO.gPos * VertPos, 0.0, 1.0);
#else
gl_Position = vec4(gPosBO.gPos * vec4(inVertex, 0.0, 1.0), 0.0, 1.0);
#endif
#ifdef TW_TILE_TEXTURED
TexCoord = inVertexTexCoord;
#endif
}

View file

@ -104,7 +104,8 @@ function build_for_type() {
-DTOOLS=OFF \
-DDEV=TRUE \
-DCMAKE_CROSSCOMPILING=ON \
-DPREFER_BUNDLED_LIBS=ON
-DPREFER_BUNDLED_LIBS=ON \
-DVULKAN=ON
(
cd "build_android/$_ANDROID_SUB_BUILD_DIR/$1" || exit 1
cmake --build . --target DDNet

View file

@ -0,0 +1,41 @@
#include "backend_base.h"
#include "engine/shared/image_manipulation.h"
size_t CCommandProcessorFragment_GLBase::TexFormatToImageColorChannelCount(int TexFormat)
{
if(TexFormat == CCommandBuffer::TEXFORMAT_RGBA)
return 4;
return 4;
}
void *CCommandProcessorFragment_GLBase::Resize(const unsigned char *pData, int Width, int Height, int NewWidth, int NewHeight, int BPP)
{
return ResizeImage((const uint8_t *)pData, Width, Height, NewWidth, NewHeight, BPP);
}
bool CCommandProcessorFragment_GLBase::Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, int ImageColorChannelCount, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight)
{
Target3DImageWidth = ImageWidth / SplitCountWidth;
Target3DImageHeight = ImageHeight / SplitCountHeight;
size_t FullImageWidth = (size_t)ImageWidth * ImageColorChannelCount;
for(int Y = 0; Y < SplitCountHeight; ++Y)
{
for(int X = 0; X < SplitCountWidth; ++X)
{
for(int Y3D = 0; Y3D < Target3DImageHeight; ++Y3D)
{
int DepthIndex = X + Y * SplitCountWidth;
size_t TargetImageFullWidth = (size_t)Target3DImageWidth * ImageColorChannelCount;
size_t TargetImageFullSize = (size_t)TargetImageFullWidth * Target3DImageHeight;
ptrdiff_t ImageOffset = (ptrdiff_t)(((size_t)Y * FullImageWidth * (size_t)Target3DImageHeight) + ((size_t)Y3D * FullImageWidth) + ((size_t)X * TargetImageFullWidth));
ptrdiff_t TargetImageOffset = (ptrdiff_t)(TargetImageFullSize * (size_t)DepthIndex + ((size_t)Y3D * TargetImageFullWidth));
mem_copy(((uint8_t *)pTarget3DImageData) + TargetImageOffset, ((uint8_t *)pImageBuffer) + (ptrdiff_t)(ImageOffset), TargetImageFullWidth);
}
}
}
return true;
}

View file

@ -0,0 +1,111 @@
#ifndef ENGINE_CLIENT_BACKEND_BACKEND_BASE_H
#define ENGINE_CLIENT_BACKEND_BACKEND_BASE_H
#include "../backend_sdl.h"
#include "engine/graphics.h"
#include <vector>
enum EDebugGFXModes
{
DEBUG_GFX_MODE_NONE = 0,
DEBUG_GFX_MODE_MINIMUM,
DEBUG_GFX_MODE_AFFECTS_PERFORMANCE,
DEBUG_GFX_MODE_VERBOSE,
DEBUG_GFX_MODE_ALL,
};
class CCommandProcessorFragment_GLBase
{
protected:
static size_t TexFormatToImageColorChannelCount(int TexFormat);
static void *Resize(const unsigned char *pData, int Width, int Height, int NewWidth, int NewHeight, int BPP);
static bool Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, int ImageColorChannelCount, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight);
virtual bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector<uint8_t> &DstData) = 0;
public:
virtual ~CCommandProcessorFragment_GLBase() = default;
virtual bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand) = 0;
virtual void StartCommands(size_t CommandCount, size_t EstimatedRenderCallCount) {}
virtual void EndCommands() {}
enum
{
CMD_PRE_INIT = CCommandBuffer::CMDGROUP_PLATFORM_GL,
CMD_INIT,
CMD_SHUTDOWN,
CMD_POST_SHUTDOWN,
};
struct SCommand_PreInit : public CCommandBuffer::SCommand
{
SCommand_PreInit() :
SCommand(CMD_PRE_INIT) {}
SDL_Window *m_pWindow;
uint32_t m_Width;
uint32_t m_Height;
char *m_pVendorString;
char *m_pVersionString;
char *m_pRendererString;
TTWGraphicsGPUList *m_pGPUList;
};
struct SCommand_Init : public CCommandBuffer::SCommand
{
SCommand_Init() :
SCommand(CMD_INIT) {}
SDL_Window *m_pWindow;
uint32_t m_Width;
uint32_t m_Height;
class IStorage *m_pStorage;
std::atomic<uint64_t> *m_pTextureMemoryUsage;
std::atomic<uint64_t> *m_pBufferMemoryUsage;
std::atomic<uint64_t> *m_pStreamMemoryUsage;
std::atomic<uint64_t> *m_pStagingMemoryUsage;
TTWGraphicsGPUList *m_pGPUList;
TGLBackendReadPresentedImageData *m_pReadPresentedImageDataFunc;
SBackendCapabilites *m_pCapabilities;
int *m_pInitError;
const char **m_pErrStringPtr;
char *m_pVendorString;
char *m_pVersionString;
char *m_pRendererString;
int m_RequestedMajor;
int m_RequestedMinor;
int m_RequestedPatch;
EBackendType m_RequestedBackend;
int m_GlewMajor;
int m_GlewMinor;
int m_GlewPatch;
};
struct SCommand_Shutdown : public CCommandBuffer::SCommand
{
SCommand_Shutdown() :
SCommand(CMD_SHUTDOWN) {}
};
struct SCommand_PostShutdown : public CCommandBuffer::SCommand
{
SCommand_PostShutdown() :
SCommand(CMD_POST_SHUTDOWN) {}
};
};
#endif

View file

@ -1,4 +1,5 @@
#include "backend_opengl.h"
#include "engine/graphics.h"
#include <base/detect.h>
@ -29,6 +30,11 @@
// ------------ CCommandProcessorFragment_OpenGL
void CCommandProcessorFragment_OpenGL::Cmd_Update_Viewport(const CCommandBuffer::SCommand_Update_Viewport *pCommand)
{
if(pCommand->m_ByResize)
{
m_CanvasWidth = (uint32_t)pCommand->m_Width;
m_CanvasHeight = (uint32_t)pCommand->m_Height;
}
glViewport(pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height);
}
@ -37,60 +43,23 @@ void CCommandProcessorFragment_OpenGL::Cmd_Finish(const CCommandBuffer::SCommand
glFinish();
}
bool CCommandProcessorFragment_OpenGL::Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, int ImageColorChannelCount, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight)
{
Target3DImageWidth = ImageWidth / SplitCountWidth;
Target3DImageHeight = ImageHeight / SplitCountHeight;
size_t FullImageWidth = (size_t)ImageWidth * ImageColorChannelCount;
for(int Y = 0; Y < SplitCountHeight; ++Y)
{
for(int X = 0; X < SplitCountWidth; ++X)
{
for(int Y3D = 0; Y3D < Target3DImageHeight; ++Y3D)
{
int DepthIndex = X + Y * SplitCountWidth;
size_t TargetImageFullWidth = (size_t)Target3DImageWidth * ImageColorChannelCount;
size_t TargetImageFullSize = (size_t)TargetImageFullWidth * Target3DImageHeight;
ptrdiff_t ImageOffset = (ptrdiff_t)(((size_t)Y * FullImageWidth * (size_t)Target3DImageHeight) + ((size_t)Y3D * FullImageWidth) + ((size_t)X * TargetImageFullWidth));
ptrdiff_t TargetImageOffset = (ptrdiff_t)(TargetImageFullSize * (size_t)DepthIndex + ((size_t)Y3D * TargetImageFullWidth));
mem_copy(((uint8_t *)pTarget3DImageData) + TargetImageOffset, ((uint8_t *)pImageBuffer) + (ptrdiff_t)(ImageOffset), TargetImageFullWidth);
}
}
}
return true;
}
int CCommandProcessorFragment_OpenGL::TexFormatToOpenGLFormat(int TexFormat)
{
if(TexFormat == CCommandBuffer::TEXFORMAT_RGB)
return GL_RGB;
if(TexFormat == CCommandBuffer::TEXFORMAT_ALPHA)
return GL_ALPHA;
if(TexFormat == CCommandBuffer::TEXFORMAT_RGBA)
return GL_RGBA;
return GL_RGBA;
}
int CCommandProcessorFragment_OpenGL::TexFormatToImageColorChannelCount(int TexFormat)
size_t CCommandProcessorFragment_OpenGL::GLFormatToImageColorChannelCount(int GLFormat)
{
if(TexFormat == CCommandBuffer::TEXFORMAT_RGB)
return 3;
if(TexFormat == CCommandBuffer::TEXFORMAT_ALPHA)
return 1;
if(TexFormat == CCommandBuffer::TEXFORMAT_RGBA)
return 4;
return 4;
}
void *CCommandProcessorFragment_OpenGL::Resize(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData)
{
int Bpp = TexFormatToImageColorChannelCount(Format);
return ResizeImage((const uint8_t *)pData, Width, Height, NewWidth, NewHeight, Bpp);
switch(GLFormat)
{
case GL_RGBA: return 4;
case GL_RGB: return 3;
case GL_RED: return 1;
case GL_ALPHA: return 1;
default: return 4;
}
}
bool CCommandProcessorFragment_OpenGL::IsTexturedState(const CCommandBuffer::SState &State)
@ -312,10 +281,44 @@ GfxOpenGLMessageCallback(GLenum source,
}
#endif
bool CCommandProcessorFragment_OpenGL::GetPresentedImageData(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector<uint8_t> &DstData)
{
if(m_CanvasWidth == 0 || m_CanvasHeight == 0)
{
return false;
}
else
{
Width = m_CanvasWidth;
Height = m_CanvasHeight;
Format = CImageInfo::FORMAT_RGBA;
DstData.resize((size_t)Width * (Height + 1) * 4); // +1 for flipping image
glReadBuffer(GL_FRONT);
GLint Alignment;
glGetIntegerv(GL_PACK_ALIGNMENT, &Alignment);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, m_CanvasWidth, m_CanvasHeight, GL_RGBA, GL_UNSIGNED_BYTE, DstData.data());
glPixelStorei(GL_PACK_ALIGNMENT, Alignment);
uint8_t *pTempRow = DstData.data() + Width * Height * 4;
for(uint32_t Y = 0; Y < Height / 2; ++Y)
{
mem_copy(pTempRow, DstData.data() + Y * Width * 4, Width * 4);
mem_copy(DstData.data() + Y * Width * 4, DstData.data() + ((Height - Y) - 1) * Width * 4, Width * 4);
mem_copy(DstData.data() + ((Height - Y) - 1) * Width * 4, pTempRow, Width * 4);
}
return true;
}
}
bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand)
{
m_IsOpenGLES = pCommand->m_RequestedBackend == BACKEND_TYPE_OPENGL_ES;
TGLBackendReadPresentedImageData &ReadPresentedImgDataFunc = *pCommand->m_pReadPresentedImageDataFunc;
ReadPresentedImgDataFunc = [this](uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector<uint8_t> &DstData) { return GetPresentedImageData(Width, Height, Format, DstData); };
const char *pVendorString = (const char *)glGetString(GL_VENDOR);
dbg_msg("opengl", "Vendor string: %s", pVendorString);
@ -337,7 +340,7 @@ bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand)
int BlocklistMajor = -1, BlocklistMinor = -1, BlocklistPatch = -1;
bool RequiresWarning = false;
const char *pErrString = ParseBlocklistDriverVersions(pVendorString, pVersionString, BlocklistMajor, BlocklistMinor, BlocklistPatch, RequiresWarning);
//if the driver is buggy, and the requested GL version is the default, fallback
// if the driver is buggy, and the requested GL version is the default, fallback
if(pErrString != NULL && pCommand->m_RequestedMajor == 3 && pCommand->m_RequestedMinor == 0 && pCommand->m_RequestedPatch == 0)
{
// if not already in the error state, set the GL version
@ -405,6 +408,7 @@ bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand)
pCommand->m_pCapabilities->m_2DArrayTexturesAsExtension = false;
pCommand->m_pCapabilities->m_NPOTTextures = true;
pCommand->m_pCapabilities->m_TrianglesAsQuads = false;
if(MajorV >= 4 || (MajorV == 3 && MinorV == 3))
{
@ -417,6 +421,8 @@ bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand)
pCommand->m_pCapabilities->m_MipMapping = true;
pCommand->m_pCapabilities->m_3DTextures = true;
pCommand->m_pCapabilities->m_2DArrayTextures = true;
pCommand->m_pCapabilities->m_TrianglesAsQuads = true;
}
else if(MajorV == 3)
{
@ -563,7 +569,7 @@ bool CCommandProcessorFragment_OpenGL::InitOpenGL(const SCommand_Init *pCommand)
glDepthMask(0);
#ifndef BACKEND_AS_OPENGL_ES
if(g_Config.m_DbgGfx)
if(g_Config.m_DbgGfx != DEBUG_GFX_MODE_NONE)
{
if(GLEW_KHR_debug || GLEW_ARB_debug_output)
{
@ -624,26 +630,20 @@ bool CCommandProcessorFragment_OpenGL::Cmd_Init(const SCommand_Init *pCommand)
return true;
}
void CCommandProcessorFragment_OpenGL::Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand)
void CCommandProcessorFragment_OpenGL::TextureUpdate(int Slot, int X, int Y, int Width, int Height, int GLFormat, void *pTexData)
{
glBindTexture(GL_TEXTURE_2D, m_Textures[pCommand->m_Slot].m_Tex);
void *pTexData = pCommand->m_pData;
int Width = pCommand->m_Width;
int Height = pCommand->m_Height;
int X = pCommand->m_X;
int Y = pCommand->m_Y;
glBindTexture(GL_TEXTURE_2D, m_Textures[Slot].m_Tex);
if(!m_HasNPOTTextures)
{
float ResizeW = m_Textures[pCommand->m_Slot].m_ResizeWidth;
float ResizeH = m_Textures[pCommand->m_Slot].m_ResizeHeight;
float ResizeW = m_Textures[Slot].m_ResizeWidth;
float ResizeH = m_Textures[Slot].m_ResizeHeight;
if(ResizeW > 0 && ResizeH > 0)
{
int ResizedW = (int)(Width * ResizeW);
int ResizedH = (int)(Height * ResizeH);
void *pTmpData = Resize(Width, Height, ResizedW, ResizedH, pCommand->m_Format, static_cast<const unsigned char *>(pTexData));
void *pTmpData = Resize(static_cast<const unsigned char *>(pTexData), Width, Height, ResizedW, ResizedH, GLFormatToImageColorChannelCount(GLFormat));
free(pTexData);
pTexData = pTmpData;
@ -652,11 +652,11 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Update(const CCommandBuffer::
}
}
if(m_Textures[pCommand->m_Slot].m_RescaleCount > 0)
if(m_Textures[Slot].m_RescaleCount > 0)
{
int OldWidth = Width;
int OldHeight = Height;
for(int i = 0; i < m_Textures[pCommand->m_Slot].m_RescaleCount; ++i)
for(int i = 0; i < m_Textures[Slot].m_RescaleCount; ++i)
{
Width >>= 1;
Height >>= 1;
@ -665,16 +665,20 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Update(const CCommandBuffer::
Y /= 2;
}
void *pTmpData = Resize(OldWidth, OldHeight, Width, Height, pCommand->m_Format, static_cast<const unsigned char *>(pTexData));
void *pTmpData = Resize(static_cast<const unsigned char *>(pTexData), OldWidth, OldHeight, Width, Height, GLFormatToImageColorChannelCount(GLFormat));
free(pTexData);
pTexData = pTmpData;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, X, Y, Width, Height,
TexFormatToOpenGLFormat(pCommand->m_Format), GL_UNSIGNED_BYTE, pTexData);
glTexSubImage2D(GL_TEXTURE_2D, 0, X, Y, Width, Height, GLFormat, GL_UNSIGNED_BYTE, pTexData);
free(pTexData);
}
void CCommandProcessorFragment_OpenGL::Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand)
{
TextureUpdate(pCommand->m_Slot, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height, TexFormatToOpenGLFormat(pCommand->m_Format), pCommand->m_pData);
}
void CCommandProcessorFragment_OpenGL::DestroyTexture(int Slot)
{
m_pTextureMemoryUsage->store(m_pTextureMemoryUsage->load(std::memory_order_relaxed) - m_Textures[Slot].m_MemSize, std::memory_order_relaxed);
@ -713,12 +717,9 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Destroy(const CCommandBuffer:
DestroyTexture(pCommand->m_Slot);
}
void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand)
void CCommandProcessorFragment_OpenGL::TextureCreate(int Slot, int Width, int Height, int PixelSize, int GLFormat, int GLStoreFormat, int Flags, void *pTexData)
{
#ifndef BACKEND_GL_MODERN_API
int Width = pCommand->m_Width;
int Height = pCommand->m_Height;
void *pTexData = pCommand->m_pData;
if(m_MaxTexSize == -1)
{
@ -727,11 +728,11 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_MaxTexSize);
}
if(pCommand->m_Slot >= (int)m_Textures.size())
if(Slot >= (int)m_Textures.size())
m_Textures.resize(m_Textures.size() * 2);
m_Textures[pCommand->m_Slot].m_ResizeWidth = -1.f;
m_Textures[pCommand->m_Slot].m_ResizeHeight = -1.f;
m_Textures[Slot].m_ResizeWidth = -1.f;
m_Textures[Slot].m_ResizeHeight = -1.f;
if(!m_HasNPOTTextures)
{
@ -739,12 +740,12 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
int PowerOfTwoHeight = HighestBit(Height);
if(Width != PowerOfTwoWidth || Height != PowerOfTwoHeight)
{
void *pTmpData = Resize(Width, Height, PowerOfTwoWidth, PowerOfTwoHeight, pCommand->m_Format, static_cast<const unsigned char *>(pTexData));
void *pTmpData = Resize(static_cast<const unsigned char *>(pTexData), Width, Height, PowerOfTwoWidth, PowerOfTwoHeight, GLFormatToImageColorChannelCount(GLFormat));
free(pTexData);
pTexData = pTmpData;
m_Textures[pCommand->m_Slot].m_ResizeWidth = (float)PowerOfTwoWidth / (float)Width;
m_Textures[pCommand->m_Slot].m_ResizeHeight = (float)PowerOfTwoHeight / (float)Height;
m_Textures[Slot].m_ResizeWidth = (float)PowerOfTwoWidth / (float)Width;
m_Textures[Slot].m_ResizeHeight = (float)PowerOfTwoHeight / (float)Height;
Width = PowerOfTwoWidth;
Height = PowerOfTwoHeight;
@ -752,7 +753,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
}
int RescaleCount = 0;
if(pCommand->m_Format == CCommandBuffer::TEXFORMAT_RGBA || pCommand->m_Format == CCommandBuffer::TEXFORMAT_RGB || pCommand->m_Format == CCommandBuffer::TEXFORMAT_ALPHA)
if(GLFormat == GL_RGBA)
{
int OldWidth = Width;
int OldHeight = Height;
@ -771,27 +772,27 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
if(NeedsResize)
{
void *pTmpData = Resize(OldWidth, OldHeight, Width, Height, pCommand->m_Format, static_cast<const unsigned char *>(pTexData));
void *pTmpData = Resize(static_cast<const unsigned char *>(pTexData), OldWidth, OldHeight, Width, Height, GLFormatToImageColorChannelCount(GLFormat));
free(pTexData);
pTexData = pTmpData;
}
}
m_Textures[pCommand->m_Slot].m_Width = Width;
m_Textures[pCommand->m_Slot].m_Height = Height;
m_Textures[pCommand->m_Slot].m_RescaleCount = RescaleCount;
m_Textures[Slot].m_Width = Width;
m_Textures[Slot].m_Height = Height;
m_Textures[Slot].m_RescaleCount = RescaleCount;
int Oglformat = TexFormatToOpenGLFormat(pCommand->m_Format);
int StoreOglformat = TexFormatToOpenGLFormat(pCommand->m_StoreFormat);
int Oglformat = GLFormat;
int StoreOglformat = GLStoreFormat;
if((pCommand->m_Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
{
glGenTextures(1, &m_Textures[pCommand->m_Slot].m_Tex);
glBindTexture(GL_TEXTURE_2D, m_Textures[pCommand->m_Slot].m_Tex);
glGenTextures(1, &m_Textures[Slot].m_Tex);
glBindTexture(GL_TEXTURE_2D, m_Textures[Slot].m_Tex);
}
if(pCommand->m_Flags & CCommandBuffer::TEXFLAG_NOMIPMAPS || !m_HasMipMaps)
if(Flags & CCommandBuffer::TEXFLAG_NOMIPMAPS || !m_HasMipMaps)
{
if((pCommand->m_Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@ -800,7 +801,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
}
else
{
if((pCommand->m_Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
@ -816,11 +817,11 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
int Flag2DArrayTexture = (CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE | CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER);
int Flag3DTexture = (CCommandBuffer::TEXFLAG_TO_3D_TEXTURE | CCommandBuffer::TEXFLAG_TO_3D_TEXTURE_SINGLE_LAYER);
if((pCommand->m_Flags & (Flag2DArrayTexture | Flag3DTexture)) != 0)
if((Flags & (Flag2DArrayTexture | Flag3DTexture)) != 0)
{
bool Is3DTexture = (pCommand->m_Flags & Flag3DTexture) != 0;
bool Is3DTexture = (Flags & Flag3DTexture) != 0;
glGenTextures(1, &m_Textures[pCommand->m_Slot].m_Tex2DArray);
glGenTextures(1, &m_Textures[Slot].m_Tex2DArray);
GLenum Target = GL_TEXTURE_3D;
@ -833,12 +834,12 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
Target = m_2DArrayTarget;
}
glBindTexture(Target, m_Textures[pCommand->m_Slot].m_Tex2DArray);
glBindTexture(Target, m_Textures[Slot].m_Tex2DArray);
if(IsNewApi())
{
glGenSamplers(1, &m_Textures[pCommand->m_Slot].m_Sampler2DArray);
glBindSampler(0, m_Textures[pCommand->m_Slot].m_Sampler2DArray);
glGenSamplers(1, &m_Textures[Slot].m_Sampler2DArray);
glBindSampler(0, m_Textures[Slot].m_Sampler2DArray);
}
glTexParameteri(Target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@ -846,14 +847,14 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
{
glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if(IsNewApi())
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
else
{
glTexParameteri(Target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(Target, GL_GENERATE_MIPMAP, GL_TRUE);
if(IsNewApi())
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
glTexParameteri(Target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@ -867,23 +868,23 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
if(IsNewApi())
{
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
#ifndef BACKEND_AS_OPENGL_ES
if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES)
glSamplerParameterf(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
glSamplerParameterf(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
#endif
glBindSampler(0, 0);
}
int ImageColorChannels = TexFormatToImageColorChannelCount(pCommand->m_Format);
int ImageColorChannels = GLFormatToImageColorChannelCount(GLFormat);
uint8_t *p3DImageData = NULL;
bool IsSingleLayer = (pCommand->m_Flags & (CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER | CCommandBuffer::TEXFLAG_TO_3D_TEXTURE_SINGLE_LAYER)) != 0;
bool IsSingleLayer = (Flags & (CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER | CCommandBuffer::TEXFLAG_TO_3D_TEXTURE_SINGLE_LAYER)) != 0;
if(!IsSingleLayer)
p3DImageData = (uint8_t *)malloc((size_t)ImageColorChannels * Width * Height);
@ -899,7 +900,7 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
dbg_msg("gfx", "3D/2D array texture was resized");
int NewWidth = maximum<int>(HighestBit(ConvertWidth), 16);
int NewHeight = maximum<int>(HighestBit(ConvertHeight), 16);
uint8_t *pNewTexData = (uint8_t *)Resize(ConvertWidth, ConvertHeight, NewWidth, NewHeight, pCommand->m_Format, (const uint8_t *)pTexData);
uint8_t *pNewTexData = (uint8_t *)Resize((const uint8_t *)pTexData, ConvertWidth, ConvertHeight, NewWidth, NewHeight, GLFormatToImageColorChannelCount(GLFormat));
ConvertWidth = NewWidth;
ConvertHeight = NewHeight;
@ -927,22 +928,46 @@ void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::
}
// This is the initial value for the wrap modes
m_Textures[pCommand->m_Slot].m_LastWrapMode = CCommandBuffer::WRAP_REPEAT;
m_Textures[Slot].m_LastWrapMode = CCommandBuffer::WRAP_REPEAT;
// calculate memory usage
m_Textures[pCommand->m_Slot].m_MemSize = Width * Height * pCommand->m_PixelSize;
m_Textures[Slot].m_MemSize = Width * Height * PixelSize;
while(Width > 2 && Height > 2)
{
Width >>= 1;
Height >>= 1;
m_Textures[pCommand->m_Slot].m_MemSize += Width * Height * pCommand->m_PixelSize;
m_Textures[Slot].m_MemSize += Width * Height * PixelSize;
}
m_pTextureMemoryUsage->store(m_pTextureMemoryUsage->load(std::memory_order_relaxed) + m_Textures[pCommand->m_Slot].m_MemSize, std::memory_order_relaxed);
m_pTextureMemoryUsage->store(m_pTextureMemoryUsage->load(std::memory_order_relaxed) + m_Textures[Slot].m_MemSize, std::memory_order_relaxed);
free(pTexData);
#endif
}
void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand)
{
TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, pCommand->m_PixelSize, TexFormatToOpenGLFormat(pCommand->m_Format), TexFormatToOpenGLFormat(pCommand->m_StoreFormat), pCommand->m_Flags, pCommand->m_pData);
}
void CCommandProcessorFragment_OpenGL::Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand)
{
TextureUpdate(pCommand->m_Slot, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height, GL_ALPHA, pCommand->m_pData);
}
void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Destroy(const CCommandBuffer::SCommand_TextTextures_Destroy *pCommand)
{
DestroyTexture(pCommand->m_Slot);
DestroyTexture(pCommand->m_SlotOutline);
}
void CCommandProcessorFragment_OpenGL::Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand)
{
void *pTextData = pCommand->m_pTextData;
void *pTextOutlineData = pCommand->m_pTextOutlineData;
TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, 1, GL_ALPHA, GL_ALPHA, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTextData);
TextureCreate(pCommand->m_SlotOutline, pCommand->m_Width, pCommand->m_Height, 1, GL_ALPHA, GL_ALPHA, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTextOutlineData);
}
void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand)
{
glClearColor(pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, 0.0f);
@ -980,8 +1005,10 @@ void CCommandProcessorFragment_OpenGL::Cmd_Render(const CCommandBuffer::SCommand
#endif
}
void CCommandProcessorFragment_OpenGL::Cmd_Screenshot(const CCommandBuffer::SCommand_Screenshot *pCommand)
void CCommandProcessorFragment_OpenGL::Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand)
{
*pCommand->m_pSwapped = false;
// fetch image data
GLint aViewport[4] = {0, 0, 0, 0};
glGetIntegerv(GL_VIEWPORT, aViewport);
@ -1045,6 +1072,15 @@ bool CCommandProcessorFragment_OpenGL::RunCommand(const CCommandBuffer::SCommand
case CCommandBuffer::CMD_TEXTURE_UPDATE:
Cmd_Texture_Update(static_cast<const CCommandBuffer::SCommand_Texture_Update *>(pBaseCommand));
break;
case CCommandBuffer::CMD_TEXT_TEXTURES_CREATE:
Cmd_TextTextures_Create(static_cast<const CCommandBuffer::SCommand_TextTextures_Create *>(pBaseCommand));
break;
case CCommandBuffer::CMD_TEXT_TEXTURES_DESTROY:
Cmd_TextTextures_Destroy(static_cast<const CCommandBuffer::SCommand_TextTextures_Destroy *>(pBaseCommand));
break;
case CCommandBuffer::CMD_TEXT_TEXTURE_UPDATE:
Cmd_TextTexture_Update(static_cast<const CCommandBuffer::SCommand_TextTexture_Update *>(pBaseCommand));
break;
case CCommandBuffer::CMD_CLEAR:
Cmd_Clear(static_cast<const CCommandBuffer::SCommand_Clear *>(pBaseCommand));
break;
@ -1054,8 +1090,8 @@ bool CCommandProcessorFragment_OpenGL::RunCommand(const CCommandBuffer::SCommand
case CCommandBuffer::CMD_RENDER_TEX3D:
Cmd_RenderTex3D(static_cast<const CCommandBuffer::SCommand_RenderTex3D *>(pBaseCommand));
break;
case CCommandBuffer::CMD_SCREENSHOT:
Cmd_Screenshot(static_cast<const CCommandBuffer::SCommand_Screenshot *>(pBaseCommand));
case CCommandBuffer::CMD_TRY_SWAP_AND_SCREENSHOT:
Cmd_Screenshot(static_cast<const CCommandBuffer::SCommand_TrySwapAndScreenshot *>(pBaseCommand));
break;
case CCommandBuffer::CMD_UPDATE_VIEWPORT:
Cmd_Update_Viewport(static_cast<const CCommandBuffer::SCommand_Update_Viewport *>(pBaseCommand));
@ -1080,7 +1116,6 @@ bool CCommandProcessorFragment_OpenGL::RunCommand(const CCommandBuffer::SCommand
case CCommandBuffer::CMD_RENDER_BORDER_TILE_LINE: Cmd_RenderBorderTileLine(static_cast<const CCommandBuffer::SCommand_RenderBorderTileLine *>(pBaseCommand)); break;
case CCommandBuffer::CMD_RENDER_QUAD_LAYER: Cmd_RenderQuadLayer(static_cast<const CCommandBuffer::SCommand_RenderQuadLayer *>(pBaseCommand)); break;
case CCommandBuffer::CMD_RENDER_TEXT: Cmd_RenderText(static_cast<const CCommandBuffer::SCommand_RenderText *>(pBaseCommand)); break;
case CCommandBuffer::CMD_RENDER_TEXT_STREAM: Cmd_RenderTextStream(static_cast<const CCommandBuffer::SCommand_RenderTextStream *>(pBaseCommand)); break;
case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER: Cmd_RenderQuadContainer(static_cast<const CCommandBuffer::SCommand_RenderQuadContainer *>(pBaseCommand)); break;
case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_EX: Cmd_RenderQuadContainerEx(static_cast<const CCommandBuffer::SCommand_RenderQuadContainerEx *>(pBaseCommand)); break;
case CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_SPRITE_MULTIPLE: Cmd_RenderQuadContainerAsSpriteMultiple(static_cast<const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *>(pBaseCommand)); break;
@ -1103,7 +1138,7 @@ bool CCommandProcessorFragment_OpenGL2::IsAndUpdateTextureSlotBound(int IDX, int
return true;
else
{
//the texture slot uses this index now
// the texture slot uses this index now
m_TextureSlotBoundToUnit[IDX].m_TextureSlot = Slot;
m_TextureSlotBoundToUnit[IDX].m_Is2DArray = Is2DArray;
return false;
@ -1125,14 +1160,14 @@ void CCommandProcessorFragment_OpenGL2::SetState(const CCommandBuffer::SState &S
{
case CCommandBuffer::BLEND_NONE:
// We don't really need this anymore
//glDisable(GL_BLEND);
// glDisable(GL_BLEND);
break;
case CCommandBuffer::BLEND_ALPHA:
//glEnable(GL_BLEND);
// glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
case CCommandBuffer::BLEND_ADDITIVE:
//glEnable(GL_BLEND);
// glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
break;
default:
@ -1267,10 +1302,14 @@ void CCommandProcessorFragment_OpenGL2::SetState(const CCommandBuffer::SState &S
// orthographic projection matrix
// the z coordinate is the same for every vertex, so just ignore the z coordinate and set it in the shaders
float m[2 * 4] = {
2.f / (State.m_ScreenBR.x - State.m_ScreenTL.x), 0, 0, -((State.m_ScreenBR.x + State.m_ScreenTL.x) / (State.m_ScreenBR.x - State.m_ScreenTL.x)),
0, (2.f / (State.m_ScreenTL.y - State.m_ScreenBR.y)), 0, -((State.m_ScreenTL.y + State.m_ScreenBR.y) / (State.m_ScreenTL.y - State.m_ScreenBR.y)),
//0, 0, -(2.f/(9.f)), -((11.f)/(9.f)),
//0, 0, 0, 1.0f
2.f / (State.m_ScreenBR.x - State.m_ScreenTL.x),
0,
0,
-((State.m_ScreenBR.x + State.m_ScreenTL.x) / (State.m_ScreenBR.x - State.m_ScreenTL.x)),
0,
(2.f / (State.m_ScreenTL.y - State.m_ScreenBR.y)),
0,
-((State.m_ScreenTL.y + State.m_ScreenBR.y) / (State.m_ScreenTL.y - State.m_ScreenBR.y)),
};
// transpose bcs of column-major order of opengl
@ -1580,7 +1619,7 @@ bool CCommandProcessorFragment_OpenGL2::IsTileMapAnalysisSucceeded()
}
}
//everything build up, now do the analyze steps
// everything build up, now do the analyze steps
bool NoError = DoAnalyzeStep(0, CheckCount, VertexCount, pFakeTexture, SingleImageSize);
if(NoError && m_HasShaders)
NoError &= DoAnalyzeStep(1, CheckCount, VertexCount, pFakeTexture, SingleImageSize);
@ -1596,7 +1635,7 @@ bool CCommandProcessorFragment_OpenGL2::Cmd_Init(const SCommand_Init *pCommand)
if(!CCommandProcessorFragment_OpenGL::Cmd_Init(pCommand))
return false;
m_OpenGLTextureLodBIAS = g_Config.m_GfxOpenGLTextureLODBIAS;
m_OpenGLTextureLodBIAS = g_Config.m_GfxGLTextureLODBIAS;
m_HasShaders = pCommand->m_pCapabilities->m_ShaderSupport;
@ -1635,7 +1674,7 @@ bool CCommandProcessorFragment_OpenGL2::Cmd_Init(const SCommand_Init *pCommand)
m_pPrimitive3DProgram = new CGLSLPrimitiveProgram;
m_pPrimitive3DProgramTextured = new CGLSLPrimitiveProgram;
CGLSLCompiler ShaderCompiler(g_Config.m_GfxOpenGLMajor, g_Config.m_GfxOpenGLMinor, g_Config.m_GfxOpenGLPatch, m_IsOpenGLES, m_OpenGLTextureLodBIAS / 1000.0f);
CGLSLCompiler ShaderCompiler(g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch, m_IsOpenGLES, m_OpenGLTextureLodBIAS / 1000.0f);
ShaderCompiler.SetHasTextureArray(pCommand->m_pCapabilities->m_2DArrayTextures);
if(pCommand->m_pCapabilities->m_2DArrayTextures)
@ -1819,7 +1858,7 @@ void CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferObject(const CCommandBuf
{
void *pUploadData = pCommand->m_pUploadData;
int Index = pCommand->m_BufferIndex;
//create necessary space
// create necessary space
if((size_t)Index >= m_BufferObjectIndices.size())
{
for(int i = m_BufferObjectIndices.size(); i < Index + 1; ++i)
@ -1927,13 +1966,14 @@ void CCommandProcessorFragment_OpenGL2::Cmd_DeleteBufferObject(const CCommandBuf
void CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferContainer(const CCommandBuffer::SCommand_CreateBufferContainer *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//create necessary space
// create necessary space
if((size_t)Index >= m_BufferContainers.size())
{
for(int i = m_BufferContainers.size(); i < Index + 1; ++i)
{
SBufferContainer Container;
Container.m_ContainerInfo.m_Stride = 0;
Container.m_ContainerInfo.m_VertBufferBindingIndex = -1;
m_BufferContainers.push_back(Container);
}
}
@ -1947,6 +1987,7 @@ void CCommandProcessorFragment_OpenGL2::Cmd_CreateBufferContainer(const CCommand
}
BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride;
BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex = pCommand->m_VertBufferBindingIndex;
}
void CCommandProcessorFragment_OpenGL2::Cmd_UpdateBufferContainer(const CCommandBuffer::SCommand_UpdateBufferContainer *pCommand)
@ -1962,6 +2003,7 @@ void CCommandProcessorFragment_OpenGL2::Cmd_UpdateBufferContainer(const CCommand
}
BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride;
BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex = pCommand->m_VertBufferBindingIndex;
}
void CCommandProcessorFragment_OpenGL2::Cmd_DeleteBufferContainer(const CCommandBuffer::SCommand_DeleteBufferContainer *pCommand)
@ -1970,28 +2012,16 @@ void CCommandProcessorFragment_OpenGL2::Cmd_DeleteBufferContainer(const CCommand
if(pCommand->m_DestroyAllBO)
{
for(size_t i = 0; i < BufferContainer.m_ContainerInfo.m_Attributes.size(); ++i)
int VertBufferID = BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex;
if(VertBufferID != -1)
{
int VertBufferID = BufferContainer.m_ContainerInfo.m_Attributes[i].m_VertBufferBindingIndex;
if(VertBufferID != -1)
if(m_HasShaders)
{
for(auto &Attribute : BufferContainer.m_ContainerInfo.m_Attributes)
{
// set all equal ids to zero to not double delete
if(VertBufferID == Attribute.m_VertBufferBindingIndex)
{
Attribute.m_VertBufferBindingIndex = -1;
}
}
if(m_HasShaders)
{
glDeleteBuffers(1, &m_BufferObjectIndices[VertBufferID].m_BufferObjectID);
}
free(m_BufferObjectIndices[VertBufferID].m_pData);
m_BufferObjectIndices[VertBufferID].m_pData = NULL;
glDeleteBuffers(1, &m_BufferObjectIndices[VertBufferID].m_BufferObjectID);
}
free(m_BufferObjectIndices[VertBufferID].m_pData);
m_BufferObjectIndices[VertBufferID].m_pData = NULL;
}
}
@ -2025,7 +2055,7 @@ void CCommandProcessorFragment_OpenGL2::RenderBorderTileEmulation(SBufferContain
bool IsTextured = BufferContainer.m_ContainerInfo.m_Attributes.size() == 2;
SBufferObject &BufferObject = m_BufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_Attributes[0].m_VertBufferBindingIndex];
SBufferObject &BufferObject = m_BufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex];
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
@ -2110,7 +2140,7 @@ void CCommandProcessorFragment_OpenGL2::RenderBorderTileLineEmulation(SBufferCon
bool IsTextured = BufferContainer.m_ContainerInfo.m_Attributes.size() == 2;
SBufferObject &BufferObject = m_BufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_Attributes[0].m_VertBufferBindingIndex];
SBufferObject &BufferObject = m_BufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex];
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
@ -2173,7 +2203,7 @@ void CCommandProcessorFragment_OpenGL2::RenderBorderTileLineEmulation(SBufferCon
void CCommandProcessorFragment_OpenGL2::Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -2185,7 +2215,7 @@ void CCommandProcessorFragment_OpenGL2::Cmd_RenderBorderTile(const CCommandBuffe
void CCommandProcessorFragment_OpenGL2::Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -2197,7 +2227,7 @@ void CCommandProcessorFragment_OpenGL2::Cmd_RenderBorderTileLine(const CCommandB
void CCommandProcessorFragment_OpenGL2::Cmd_RenderTileLayer(const CCommandBuffer::SCommand_RenderTileLayer *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -2205,7 +2235,7 @@ void CCommandProcessorFragment_OpenGL2::Cmd_RenderTileLayer(const CCommandBuffer
if(pCommand->m_IndicesDrawNum == 0)
{
return; //nothing to draw
return; // nothing to draw
}
if(m_HasShaders)
@ -2230,7 +2260,7 @@ void CCommandProcessorFragment_OpenGL2::Cmd_RenderTileLayer(const CCommandBuffer
bool IsTextured = BufferContainer.m_ContainerInfo.m_Attributes.size() == 2;
SBufferObject &BufferObject = m_BufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_Attributes[0].m_VertBufferBindingIndex];
SBufferObject &BufferObject = m_BufferObjectIndices[(size_t)BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex];
if(m_HasShaders)
glBindBuffer(GL_ARRAY_BUFFER, BufferObject.m_BufferObjectID);

View file

@ -12,6 +12,7 @@
#include <base/system.h>
#include <engine/client/backend/backend_base.h>
#include <engine/client/backend_sdl.h>
class CGLSLProgram;
@ -28,7 +29,7 @@ class CGLSLSpriteMultipleProgram;
#endif
// takes care of opengl related rendering
class CCommandProcessorFragment_OpenGL : public CCommandProcessorFragment_OpenGLBase
class CCommandProcessorFragment_OpenGL : public CCommandProcessorFragment_GLBase
{
protected:
struct CTexture
@ -39,9 +40,9 @@ protected:
}
TWGLuint m_Tex;
TWGLuint m_Tex2DArray; //or 3D texture as fallback
TWGLuint m_Tex2DArray; // or 3D texture as fallback
TWGLuint m_Sampler;
TWGLuint m_Sampler2DArray; //or 3D texture as fallback
TWGLuint m_Sampler2DArray; // or 3D texture as fallback
int m_LastWrapMode;
int m_MemSize;
@ -53,7 +54,10 @@ protected:
float m_ResizeHeight;
};
std::vector<CTexture> m_Textures;
std::atomic<int> *m_pTextureMemoryUsage;
std::atomic<uint64_t> *m_pTextureMemoryUsage;
uint32_t m_CanvasWidth = 0;
uint32_t m_CanvasHeight = 0;
TWGLint m_MaxTexSize;
@ -65,7 +69,7 @@ protected:
bool m_HasNPOTTextures;
bool m_HasShaders;
int m_LastBlendMode; //avoid all possible opengl state changes
int m_LastBlendMode; // avoid all possible opengl state changes
bool m_LastClipEnable;
int m_OpenGLTextureLodBIAS;
@ -73,7 +77,6 @@ protected:
bool m_IsOpenGLES;
bool IsTexturedState(const CCommandBuffer::SState &State);
static bool Texture2DTo3D(void *pImageBuffer, int ImageWidth, int ImageHeight, int ImageColorChannelCount, int SplitCountWidth, int SplitCountHeight, void *pTarget3DImageData, int &Target3DImageWidth, int &Target3DImageHeight);
bool InitOpenGL(const SCommand_Init *pCommand);
@ -81,19 +84,26 @@ protected:
virtual bool IsNewApi() { return false; }
void DestroyTexture(int Slot);
virtual bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector<uint8_t> &DstData);
static int TexFormatToOpenGLFormat(int TexFormat);
static int TexFormatToImageColorChannelCount(int TexFormat);
static void *Resize(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData);
static size_t GLFormatToImageColorChannelCount(int GLFormat);
void TextureUpdate(int Slot, int X, int Y, int Width, int Height, int GLFormat, void *pTexData);
void TextureCreate(int Slot, int Width, int Height, int PixelSize, int GLFormat, int GLStoreFormat, int Flags, void *pTexData);
virtual bool Cmd_Init(const SCommand_Init *pCommand);
virtual void Cmd_Shutdown(const SCommand_Shutdown *pCommand) {}
virtual void Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand);
virtual void Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand);
virtual void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand);
virtual void Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand);
virtual void Cmd_TextTextures_Destroy(const CCommandBuffer::SCommand_TextTextures_Destroy *pCommand);
virtual void Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand);
virtual void Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand);
virtual void Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand);
virtual void Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderTex3D"); }
virtual void Cmd_Screenshot(const CCommandBuffer::SCommand_Screenshot *pCommand);
virtual void Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand);
virtual void Cmd_Update_Viewport(const CCommandBuffer::SCommand_Update_Viewport *pCommand);
virtual void Cmd_Finish(const CCommandBuffer::SCommand_Finish *pCommand);
@ -114,7 +124,6 @@ protected:
virtual void Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderBorderTileLine"); }
virtual void Cmd_RenderQuadLayer(const CCommandBuffer::SCommand_RenderQuadLayer *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderQuadLayer"); }
virtual void Cmd_RenderText(const CCommandBuffer::SCommand_RenderText *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderText"); }
virtual void Cmd_RenderTextStream(const CCommandBuffer::SCommand_RenderTextStream *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderTextStream"); }
virtual void Cmd_RenderQuadContainer(const CCommandBuffer::SCommand_RenderQuadContainer *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderQuadContainer"); }
virtual void Cmd_RenderQuadContainerEx(const CCommandBuffer::SCommand_RenderQuadContainerEx *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderQuadContainerEx"); }
virtual void Cmd_RenderQuadContainerAsSpriteMultiple(const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *pCommand) { dbg_assert(false, "Call of unsupported Cmd_RenderQuadContainerAsSpriteMultiple"); }
@ -130,7 +139,6 @@ class CCommandProcessorFragment_OpenGL2 : public CCommandProcessorFragment_OpenG
{
struct SBufferContainer
{
SBufferContainer() {}
SBufferContainerInfo m_ContainerInfo;
};
std::vector<SBufferContainer> m_BufferContainers;
@ -202,7 +210,7 @@ protected:
int m_TextureSlot;
bool m_Is2DArray;
};
std::vector<STextureBound> m_TextureSlotBoundToUnit; //the texture index generated by loadtextureraw is stored in an index calculated by max texture units
std::vector<STextureBound> m_TextureSlotBoundToUnit; // the texture index generated by loadtextureraw is stored in an index calculated by max texture units
bool IsAndUpdateTextureSlotBound(int IDX, int Slot, bool Is2DArray = false);

View file

@ -20,10 +20,6 @@
// ------------ CCommandProcessorFragment_OpenGL3_3
int CCommandProcessorFragment_OpenGL3_3::TexFormatToNewOpenGLFormat(int TexFormat)
{
if(TexFormat == CCommandBuffer::TEXFORMAT_RGB)
return GL_RGB;
if(TexFormat == CCommandBuffer::TEXFORMAT_ALPHA)
return GL_RED;
if(TexFormat == CCommandBuffer::TEXFORMAT_RGBA)
return GL_RGBA;
return GL_RGBA;
@ -74,7 +70,7 @@ bool CCommandProcessorFragment_OpenGL3_3::Cmd_Init(const SCommand_Init *pCommand
if(!InitOpenGL(pCommand))
return false;
m_OpenGLTextureLodBIAS = g_Config.m_GfxOpenGLTextureLODBIAS;
m_OpenGLTextureLodBIAS = g_Config.m_GfxGLTextureLODBIAS;
m_UseMultipleTextureUnits = g_Config.m_GfxEnableTextureUnitOptimization;
if(!m_UseMultipleTextureUnits)
@ -114,7 +110,7 @@ bool CCommandProcessorFragment_OpenGL3_3::Cmd_Init(const SCommand_Init *pCommand
m_pSpriteProgramMultiple = new CGLSLSpriteMultipleProgram;
m_LastProgramID = 0;
CGLSLCompiler ShaderCompiler(g_Config.m_GfxOpenGLMajor, g_Config.m_GfxOpenGLMinor, g_Config.m_GfxOpenGLPatch, m_IsOpenGLES, m_OpenGLTextureLodBIAS / 1000.0f);
CGLSLCompiler ShaderCompiler(g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch, m_IsOpenGLES, m_OpenGLTextureLodBIAS / 1000.0f);
GLint CapVal;
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &CapVal);
@ -448,10 +444,10 @@ bool CCommandProcessorFragment_OpenGL3_3::Cmd_Init(const SCommand_Init *pCommand
if(m_UsePreinitializedVertexBuffer)
glBufferData(GL_ARRAY_BUFFER, sizeof(CCommandBuffer::SVertexTex3DStream) * CCommandBuffer::MAX_VERTICES, NULL, GL_STREAM_DRAW);
//query the image max size only once
// query the image max size only once
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_MaxTexSize);
//query maximum of allowed textures
// query maximum of allowed textures
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &m_MaxTextureUnits);
m_TextureSlotBoundToUnit.resize(m_MaxTextureUnits);
for(int i = 0; i < m_MaxTextureUnits; ++i)
@ -513,7 +509,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Shutdown(const SCommand_Shutdown *
m_pPrimitiveExProgramTexturedRotationless->DeleteProgram();
m_pSpriteProgramMultiple->DeleteProgram();
//clean up everything
// clean up everything
delete m_pPrimitiveProgram;
delete m_pPrimitiveProgramTextured;
delete m_pBorderTileProgram;
@ -553,27 +549,22 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Shutdown(const SCommand_Shutdown *
m_BufferContainers.clear();
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand)
void CCommandProcessorFragment_OpenGL3_3::TextureUpdate(int Slot, int X, int Y, int Width, int Height, int GLFormat, void *pTexData)
{
if(m_UseMultipleTextureUnits)
{
int Slot = pCommand->m_Slot % m_MaxTextureUnits;
//just tell, that we using this texture now
IsAndUpdateTextureSlotBound(Slot, pCommand->m_Slot);
glActiveTexture(GL_TEXTURE0 + Slot);
glBindSampler(Slot, m_Textures[pCommand->m_Slot].m_Sampler);
int SamplerSlot = Slot % m_MaxTextureUnits;
// just tell, that we using this texture now
IsAndUpdateTextureSlotBound(SamplerSlot, Slot);
glActiveTexture(GL_TEXTURE0 + SamplerSlot);
glBindSampler(SamplerSlot, m_Textures[Slot].m_Sampler);
}
glBindTexture(GL_TEXTURE_2D, m_Textures[pCommand->m_Slot].m_Tex);
glBindTexture(GL_TEXTURE_2D, m_Textures[Slot].m_Tex);
void *pTexData = pCommand->m_pData;
int Width = pCommand->m_Width;
int Height = pCommand->m_Height;
int X = pCommand->m_X;
int Y = pCommand->m_Y;
if(m_Textures[pCommand->m_Slot].m_RescaleCount > 0)
if(m_Textures[Slot].m_RescaleCount > 0)
{
for(int i = 0; i < m_Textures[pCommand->m_Slot].m_RescaleCount; ++i)
for(int i = 0; i < m_Textures[Slot].m_RescaleCount; ++i)
{
Width >>= 1;
Height >>= 1;
@ -582,16 +573,20 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Update(const CCommandBuffe
Y /= 2;
}
void *pTmpData = Resize(pCommand->m_Width, pCommand->m_Height, Width, Height, pCommand->m_Format, static_cast<const unsigned char *>(pCommand->m_pData));
void *pTmpData = Resize(static_cast<const unsigned char *>(pTexData), Width, Height, Width, Height, GLFormatToImageColorChannelCount(GLFormat));
free(pTexData);
pTexData = pTmpData;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, X, Y, Width, Height,
TexFormatToNewOpenGLFormat(pCommand->m_Format), GL_UNSIGNED_BYTE, pTexData);
glTexSubImage2D(GL_TEXTURE_2D, 0, X, Y, Width, Height, GLFormat, GL_UNSIGNED_BYTE, pTexData);
free(pTexData);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand)
{
TextureUpdate(pCommand->m_Slot, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height, TexFormatToOpenGLFormat(pCommand->m_Format), pCommand->m_pData);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand)
{
int Slot = 0;
@ -608,18 +603,14 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Destroy(const CCommandBuff
DestroyTexture(pCommand->m_Slot);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand)
void CCommandProcessorFragment_OpenGL3_3::TextureCreate(int Slot, int Width, int Height, int PixelSize, int GLFormat, int GLStoreFormat, int Flags, void *pTexData)
{
int Width = pCommand->m_Width;
int Height = pCommand->m_Height;
void *pTexData = pCommand->m_pData;
if(pCommand->m_Slot >= (int)m_Textures.size())
if(Slot >= (int)m_Textures.size())
m_Textures.resize(m_Textures.size() * 2);
// resample if needed
int RescaleCount = 0;
if(pCommand->m_Format == CCommandBuffer::TEXFORMAT_RGBA || pCommand->m_Format == CCommandBuffer::TEXFORMAT_RGB || pCommand->m_Format == CCommandBuffer::TEXFORMAT_ALPHA)
if(GLFormat == GL_RGBA)
{
if(Width > m_MaxTexSize || Height > m_MaxTexSize)
{
@ -630,64 +621,64 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffe
++RescaleCount;
} while(Width > m_MaxTexSize || Height > m_MaxTexSize);
void *pTmpData = Resize(pCommand->m_Width, pCommand->m_Height, Width, Height, pCommand->m_Format, static_cast<const unsigned char *>(pCommand->m_pData));
void *pTmpData = Resize(static_cast<const unsigned char *>(pTexData), Width, Height, Width, Height, GLFormatToImageColorChannelCount(GLFormat));
free(pTexData);
pTexData = pTmpData;
}
}
m_Textures[pCommand->m_Slot].m_Width = Width;
m_Textures[pCommand->m_Slot].m_Height = Height;
m_Textures[pCommand->m_Slot].m_RescaleCount = RescaleCount;
m_Textures[Slot].m_Width = Width;
m_Textures[Slot].m_Height = Height;
m_Textures[Slot].m_RescaleCount = RescaleCount;
int Oglformat = TexFormatToNewOpenGLFormat(pCommand->m_Format);
int StoreOglformat = TexFormatToNewOpenGLFormat(pCommand->m_StoreFormat);
int Oglformat = GLFormat;
int StoreOglformat = GLStoreFormat;
if(StoreOglformat == GL_RED)
StoreOglformat = GL_R8;
int Slot = 0;
int SamplerSlot = 0;
if(m_UseMultipleTextureUnits)
{
Slot = pCommand->m_Slot % m_MaxTextureUnits;
//just tell, that we using this texture now
IsAndUpdateTextureSlotBound(Slot, pCommand->m_Slot);
glActiveTexture(GL_TEXTURE0 + Slot);
m_TextureSlotBoundToUnit[Slot].m_TextureSlot = -1;
m_TextureSlotBoundToUnit[Slot].m_Is2DArray = false;
SamplerSlot = Slot % m_MaxTextureUnits;
// just tell, that we using this texture now
IsAndUpdateTextureSlotBound(SamplerSlot, Slot);
glActiveTexture(GL_TEXTURE0 + SamplerSlot);
m_TextureSlotBoundToUnit[SamplerSlot].m_TextureSlot = -1;
m_TextureSlotBoundToUnit[SamplerSlot].m_Is2DArray = false;
}
if((pCommand->m_Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
{
glGenTextures(1, &m_Textures[pCommand->m_Slot].m_Tex);
glBindTexture(GL_TEXTURE_2D, m_Textures[pCommand->m_Slot].m_Tex);
glGenTextures(1, &m_Textures[Slot].m_Tex);
glBindTexture(GL_TEXTURE_2D, m_Textures[Slot].m_Tex);
glGenSamplers(1, &m_Textures[pCommand->m_Slot].m_Sampler);
glBindSampler(Slot, m_Textures[pCommand->m_Slot].m_Sampler);
glGenSamplers(1, &m_Textures[Slot].m_Sampler);
glBindSampler(SamplerSlot, m_Textures[Slot].m_Sampler);
}
if(pCommand->m_Flags & CCommandBuffer::TEXFLAG_NOMIPMAPS)
if(Flags & CCommandBuffer::TEXFLAG_NOMIPMAPS)
{
if((pCommand->m_Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[Slot].m_Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[Slot].m_Sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, StoreOglformat, Width, Height, 0, Oglformat, GL_UNSIGNED_BYTE, pTexData);
}
}
else
{
if((pCommand->m_Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
if((Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0)
{
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glSamplerParameteri(m_Textures[Slot].m_Sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[Slot].m_Sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
#ifndef BACKEND_AS_OPENGL_ES
if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES)
glSamplerParameterf(m_Textures[pCommand->m_Slot].m_Sampler, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
glSamplerParameterf(m_Textures[Slot].m_Sampler, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
#endif
//prevent mipmap display bugs, when zooming out far
// prevent mipmap display bugs, when zooming out far
if(Width >= 1024 && Height >= 1024)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 5.f);
@ -697,29 +688,29 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffe
glGenerateMipmap(GL_TEXTURE_2D);
}
if((pCommand->m_Flags & (CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE | CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER)) != 0)
if((Flags & (CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE | CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER)) != 0)
{
glGenTextures(1, &m_Textures[pCommand->m_Slot].m_Tex2DArray);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_Textures[pCommand->m_Slot].m_Tex2DArray);
glGenTextures(1, &m_Textures[Slot].m_Tex2DArray);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_Textures[Slot].m_Tex2DArray);
glGenSamplers(1, &m_Textures[pCommand->m_Slot].m_Sampler2DArray);
glBindSampler(Slot, m_Textures[pCommand->m_Slot].m_Sampler2DArray);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
glGenSamplers(1, &m_Textures[Slot].m_Sampler2DArray);
glBindSampler(SamplerSlot, m_Textures[Slot].m_Sampler2DArray);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
#ifndef BACKEND_AS_OPENGL_ES
if(m_OpenGLTextureLodBIAS != 0 && !m_IsOpenGLES)
glSamplerParameterf(m_Textures[pCommand->m_Slot].m_Sampler2DArray, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
glSamplerParameterf(m_Textures[Slot].m_Sampler2DArray, GL_TEXTURE_LOD_BIAS, ((GLfloat)m_OpenGLTextureLodBIAS / 1000.0f));
#endif
int ImageColorChannels = TexFormatToImageColorChannelCount(pCommand->m_Format);
int ImageColorChannels = GLFormatToImageColorChannelCount(GLFormat);
uint8_t *p3DImageData = NULL;
bool IsSingleLayer = (pCommand->m_Flags & CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER) != 0;
bool IsSingleLayer = (Flags & CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE_SINGLE_LAYER) != 0;
if(!IsSingleLayer)
p3DImageData = (uint8_t *)malloc((size_t)ImageColorChannels * Width * Height);
@ -735,7 +726,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffe
dbg_msg("gfx", "3D/2D array texture was resized");
int NewWidth = maximum<int>(HighestBit(ConvertWidth), 16);
int NewHeight = maximum<int>(HighestBit(ConvertHeight), 16);
uint8_t *pNewTexData = (uint8_t *)Resize(ConvertWidth, ConvertHeight, NewWidth, NewHeight, pCommand->m_Format, (const uint8_t *)pTexData);
uint8_t *pNewTexData = (uint8_t *)Resize((const uint8_t *)pTexData, ConvertWidth, ConvertHeight, NewWidth, NewHeight, GLFormatToImageColorChannelCount(GLFormat));
ConvertWidth = NewWidth;
ConvertHeight = NewHeight;
@ -764,21 +755,45 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffe
}
// This is the initial value for the wrap modes
m_Textures[pCommand->m_Slot].m_LastWrapMode = CCommandBuffer::WRAP_REPEAT;
m_Textures[Slot].m_LastWrapMode = CCommandBuffer::WRAP_REPEAT;
// calculate memory usage
m_Textures[pCommand->m_Slot].m_MemSize = Width * Height * pCommand->m_PixelSize;
m_Textures[Slot].m_MemSize = Width * Height * PixelSize;
while(Width > 2 && Height > 2)
{
Width >>= 1;
Height >>= 1;
m_Textures[pCommand->m_Slot].m_MemSize += Width * Height * pCommand->m_PixelSize;
m_Textures[Slot].m_MemSize += Width * Height * PixelSize;
}
m_pTextureMemoryUsage->store(m_pTextureMemoryUsage->load(std::memory_order_relaxed) + m_Textures[pCommand->m_Slot].m_MemSize, std::memory_order_relaxed);
m_pTextureMemoryUsage->store(m_pTextureMemoryUsage->load(std::memory_order_relaxed) + m_Textures[Slot].m_MemSize, std::memory_order_relaxed);
free(pTexData);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand)
{
TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, pCommand->m_PixelSize, TexFormatToOpenGLFormat(pCommand->m_Format), TexFormatToOpenGLFormat(pCommand->m_StoreFormat), pCommand->m_Flags, pCommand->m_pData);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand)
{
TextureUpdate(pCommand->m_Slot, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height, GL_RED, pCommand->m_pData);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_TextTextures_Destroy(const CCommandBuffer::SCommand_TextTextures_Destroy *pCommand)
{
DestroyTexture(pCommand->m_Slot);
DestroyTexture(pCommand->m_SlotOutline);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand)
{
void *pTextData = pCommand->m_pTextData;
void *pTextOutlineData = pCommand->m_pTextOutlineData;
TextureCreate(pCommand->m_Slot, pCommand->m_Width, pCommand->m_Height, 1, GL_RED, GL_RED, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTextData);
TextureCreate(pCommand->m_SlotOutline, pCommand->m_Width, pCommand->m_Height, 1, GL_RED, GL_RED, CCommandBuffer::TEXFLAG_NOMIPMAPS, pTextOutlineData);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand)
{
if(pCommand->m_Color.r != m_ClearColor.r || pCommand->m_Color.g != m_ClearColor.g || pCommand->m_Color.b != m_ClearColor.b)
@ -897,22 +912,10 @@ void CCommandProcessorFragment_OpenGL3_3::DestroyBufferContainer(int Index, bool
// all buffer objects can deleted automatically, so the program doesn't need to deal with them (e.g. causing crashes because of driver bugs)
if(DeleteBOs)
{
for(size_t i = 0; i < BufferContainer.m_ContainerInfo.m_Attributes.size(); ++i)
int VertBufferID = BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex;
if(VertBufferID != -1)
{
int VertBufferID = BufferContainer.m_ContainerInfo.m_Attributes[i].m_VertBufferBindingIndex;
if(VertBufferID != -1)
{
for(auto &Attribute : BufferContainer.m_ContainerInfo.m_Attributes)
{
// set all equal ids to zero to not double delete
if(VertBufferID == Attribute.m_VertBufferBindingIndex)
{
Attribute.m_VertBufferBindingIndex = -1;
}
}
glDeleteBuffers(1, &m_BufferObjectIndices[VertBufferID]);
}
glDeleteBuffers(1, &m_BufferObjectIndices[VertBufferID]);
}
}
@ -967,7 +970,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_CreateBufferObject(const CCommandB
{
void *pUploadData = pCommand->m_pUploadData;
int Index = pCommand->m_BufferIndex;
//create necessary space
// create necessary space
if((size_t)Index >= m_BufferObjectIndices.size())
{
for(int i = m_BufferObjectIndices.size(); i < Index + 1; ++i)
@ -1032,13 +1035,14 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_DeleteBufferObject(const CCommandB
void CCommandProcessorFragment_OpenGL3_3::Cmd_CreateBufferContainer(const CCommandBuffer::SCommand_CreateBufferContainer *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//create necessary space
// create necessary space
if((size_t)Index >= m_BufferContainers.size())
{
for(int i = m_BufferContainers.size(); i < Index + 1; ++i)
{
SBufferContainer Container;
Container.m_ContainerInfo.m_Stride = 0;
Container.m_ContainerInfo.m_VertBufferBindingIndex = -1;
m_BufferContainers.push_back(Container);
}
}
@ -1053,7 +1057,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_CreateBufferContainer(const CComma
{
glEnableVertexAttribArray((GLuint)i);
glBindBuffer(GL_ARRAY_BUFFER, m_BufferObjectIndices[pCommand->m_Attributes[i].m_VertBufferBindingIndex]);
glBindBuffer(GL_ARRAY_BUFFER, m_BufferObjectIndices[pCommand->m_VertBufferBindingIndex]);
SBufferContainerInfo::SAttribute &Attr = pCommand->m_Attributes[i];
@ -1065,6 +1069,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_CreateBufferContainer(const CComma
BufferContainer.m_ContainerInfo.m_Attributes.push_back(Attr);
}
BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex = pCommand->m_VertBufferBindingIndex;
BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride;
}
@ -1074,7 +1079,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_UpdateBufferContainer(const CComma
glBindVertexArray(BufferContainer.m_VertArrayID);
//disable all old attributes
// disable all old attributes
for(size_t i = 0; i < BufferContainer.m_ContainerInfo.m_Attributes.size(); ++i)
{
glDisableVertexAttribArray((GLuint)i);
@ -1085,7 +1090,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_UpdateBufferContainer(const CComma
{
glEnableVertexAttribArray((GLuint)i);
glBindBuffer(GL_ARRAY_BUFFER, m_BufferObjectIndices[pCommand->m_Attributes[i].m_VertBufferBindingIndex]);
glBindBuffer(GL_ARRAY_BUFFER, m_BufferObjectIndices[pCommand->m_VertBufferBindingIndex]);
SBufferContainerInfo::SAttribute &Attr = pCommand->m_Attributes[i];
if(Attr.m_FuncType == 0)
glVertexAttribPointer((GLuint)i, Attr.m_DataTypeCount, Attr.m_Type, Attr.m_Normalized, pCommand->m_Stride, Attr.m_pOffset);
@ -1095,6 +1100,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_UpdateBufferContainer(const CComma
BufferContainer.m_ContainerInfo.m_Attributes.push_back(Attr);
}
BufferContainer.m_ContainerInfo.m_VertBufferBindingIndex = pCommand->m_VertBufferBindingIndex;
BufferContainer.m_ContainerInfo.m_Stride = pCommand->m_Stride;
}
@ -1112,7 +1118,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_IndicesRequiredNumNotify(const CCo
void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -1148,7 +1154,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderBorderTile(const CCommandBuf
void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -1182,7 +1188,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderBorderTileLine(const CComman
void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderTileLayer(const CCommandBuffer::SCommand_RenderTileLayer *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -1192,7 +1198,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderTileLayer(const CCommandBuff
if(pCommand->m_IndicesDrawNum == 0)
{
return; //nothing to draw
return; // nothing to draw
}
CGLSLTileProgram *pProgram = NULL;
@ -1223,7 +1229,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderTileLayer(const CCommandBuff
void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderQuadLayer(const CCommandBuffer::SCommand_RenderQuadLayer *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -1233,7 +1239,7 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderQuadLayer(const CCommandBuff
if(pCommand->m_QuadNum == 0)
{
return; //nothing to draw
return; // nothing to draw
}
CGLSLQuadProgram *pProgram = NULL;
@ -1289,7 +1295,7 @@ void CCommandProcessorFragment_OpenGL3_3::RenderText(const CCommandBuffer::SStat
{
if(DrawNum == 0)
{
return; //nothing to draw
return; // nothing to draw
}
UseProgram(m_pTextProgram);
@ -1373,7 +1379,7 @@ void CCommandProcessorFragment_OpenGL3_3::RenderText(const CCommandBuffer::SStat
void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderText(const CCommandBuffer::SCommand_RenderText *pCommand)
{
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -1391,38 +1397,15 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderText(const CCommandBuffer::S
RenderText(pCommand->m_State, pCommand->m_DrawNum, pCommand->m_TextTextureIndex, pCommand->m_TextOutlineTextureIndex, pCommand->m_TextureSize, pCommand->m_aTextColor, pCommand->m_aTextOutlineColor);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderTextStream(const CCommandBuffer::SCommand_RenderTextStream *pCommand)
{
if(pCommand->m_PrimCount == 0)
{
return; //nothing to draw
}
UploadStreamBufferData(CCommandBuffer::PRIMTYPE_QUADS, pCommand->m_pVertices, sizeof(CCommandBuffer::SVertex), pCommand->m_PrimCount);
glBindVertexArray(m_PrimitiveDrawVertexID[m_LastStreamBuffer]);
if(m_LastIndexBufferBound[m_LastStreamBuffer] != m_QuadDrawIndexBufferID)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_QuadDrawIndexBufferID);
m_LastIndexBufferBound[m_LastStreamBuffer] = m_QuadDrawIndexBufferID;
}
float aTextColor[4] = {1.f, 1.f, 1.f, 1.f};
RenderText(pCommand->m_State, pCommand->m_PrimCount * 6, pCommand->m_TextTextureIndex, pCommand->m_TextOutlineTextureIndex, pCommand->m_TextureSize, aTextColor, pCommand->m_aTextOutlineColor);
m_LastStreamBuffer = (m_LastStreamBuffer + 1 >= MAX_STREAM_BUFFER_COUNT ? 0 : m_LastStreamBuffer + 1);
}
void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderQuadContainer(const CCommandBuffer::SCommand_RenderQuadContainer *pCommand)
{
if(pCommand->m_DrawNum == 0)
{
return; //nothing to draw
return; // nothing to draw
}
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -1450,11 +1433,11 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderQuadContainerEx(const CComma
{
if(pCommand->m_DrawNum == 0)
{
return; //nothing to draw
return; // nothing to draw
}
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;
@ -1515,11 +1498,11 @@ void CCommandProcessorFragment_OpenGL3_3::Cmd_RenderQuadContainerAsSpriteMultipl
{
if(pCommand->m_DrawNum == 0 || pCommand->m_DrawCount == 0)
{
return; //nothing to draw
return; // nothing to draw
}
int Index = pCommand->m_BufferContainerIndex;
//if space not there return
// if space not there return
if((size_t)Index >= m_BufferContainers.size())
return;

View file

@ -81,11 +81,17 @@ protected:
void UploadStreamBufferData(unsigned int PrimitiveType, const void *pVertices, size_t VertSize, unsigned int PrimitiveCount, bool AsTex3D = false);
void RenderText(const CCommandBuffer::SState &State, int DrawNum, int TextTextureIndex, int TextOutlineTextureIndex, int TextureSize, const float *pTextColor, const float *pTextOutlineColor);
void TextureUpdate(int Slot, int X, int Y, int Width, int Height, int GLFormat, void *pTexData);
void TextureCreate(int Slot, int Width, int Height, int PixelSize, int GLFormat, int GLStoreFormat, int Flags, void *pTexData);
bool Cmd_Init(const SCommand_Init *pCommand) override;
void Cmd_Shutdown(const SCommand_Shutdown *pCommand) override;
void Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand) override;
void Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand) override;
void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand) override;
void Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand) override;
void Cmd_TextTextures_Destroy(const CCommandBuffer::SCommand_TextTextures_Destroy *pCommand) override;
void Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand) override;
void Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) override;
void Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand) override;
void Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand) override;
@ -106,7 +112,6 @@ protected:
void Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand) override;
void Cmd_RenderQuadLayer(const CCommandBuffer::SCommand_RenderQuadLayer *pCommand) override;
void Cmd_RenderText(const CCommandBuffer::SCommand_RenderText *pCommand) override;
void Cmd_RenderTextStream(const CCommandBuffer::SCommand_RenderTextStream *pCommand) override;
void Cmd_RenderQuadContainer(const CCommandBuffer::SCommand_RenderQuadContainer *pCommand) override;
void Cmd_RenderQuadContainerEx(const CCommandBuffer::SCommand_RenderQuadContainerEx *pCommand) override;
void Cmd_RenderQuadContainerAsSpriteMultiple(const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *pCommand) override;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
#ifndef ENGINE_CLIENT_BACKEND_VULKAN_BACKEND_VULKAN_H
#define ENGINE_CLIENT_BACKEND_VULKAN_BACKEND_VULKAN_H
#include <base/detect.h>
#include <engine/client/backend_sdl.h>
static constexpr int gs_BackendVulkanMajor = 1;
static constexpr int gs_BackendVulkanMinor = 0;
CCommandProcessorFragment_GLBase *CreateVulkanCommandProcessorFragment();
#endif

View file

@ -42,6 +42,12 @@
#include "backend/opengles/backend_opengles3.h"
#endif
#if defined(CONF_BACKEND_VULKAN)
#include <SDL_vulkan.h>
#include "backend/vulkan/backend_vulkan.h"
#endif
#include "graphics_threaded.h"
#include <engine/shared/image_manipulation.h>
@ -62,10 +68,10 @@ void CGraphicsBackend_Threaded::ThreadFunc(void *pUser)
std::unique_lock<std::mutex> Lock(pSelf->m_BufferSwapMutex);
// notify, that the thread started
pSelf->m_Started = true;
pSelf->m_BufferDoneCond.notify_all();
pSelf->m_BufferSwapCond.notify_all();
while(!pSelf->m_Shutdown)
{
pSelf->m_BufferSwapCond.wait(Lock);
pSelf->m_BufferSwapCond.wait(Lock, [&pSelf] { return pSelf->m_pBuffer != nullptr || pSelf->m_Shutdown; });
if(pSelf->m_pBuffer)
{
#ifdef CONF_PLATFORM_MACOS
@ -75,7 +81,7 @@ void CGraphicsBackend_Threaded::ThreadFunc(void *pUser)
pSelf->m_pBuffer = nullptr;
pSelf->m_BufferInProcess.store(false, std::memory_order_relaxed);
pSelf->m_BufferDoneCond.notify_all();
pSelf->m_BufferSwapCond.notify_all();
#if defined(CONF_VIDEORECORDER)
if(IVideo::Current())
@ -94,16 +100,18 @@ CGraphicsBackend_Threaded::CGraphicsBackend_Threaded()
void CGraphicsBackend_Threaded::StartProcessor(ICommandProcessor *pProcessor)
{
dbg_assert(m_Shutdown, "Processor was already not shut down.");
m_Shutdown = false;
m_pProcessor = pProcessor;
std::unique_lock<std::mutex> Lock(m_BufferSwapMutex);
m_Thread = thread_init(ThreadFunc, this, "Graphics thread");
// wait for the thread to start
m_BufferDoneCond.wait(Lock, [this]() -> bool { return m_Started; });
m_BufferSwapCond.wait(Lock, [this]() -> bool { return m_Started; });
}
void CGraphicsBackend_Threaded::StopProcessor()
{
dbg_assert(!m_Shutdown, "Processor was already shut down.");
m_Shutdown = true;
{
std::unique_lock<std::mutex> Lock(m_BufferSwapMutex);
@ -121,6 +129,11 @@ void CGraphicsBackend_Threaded::RunBuffer(CCommandBuffer *pBuffer)
m_BufferSwapCond.notify_all();
}
void CGraphicsBackend_Threaded::RunBufferSingleThreadedUnsafe(CCommandBuffer *pBuffer)
{
m_pProcessor->RunBuffer(pBuffer);
}
bool CGraphicsBackend_Threaded::IsIdle() const
{
return !m_BufferInProcess.load(std::memory_order_relaxed);
@ -129,8 +142,7 @@ bool CGraphicsBackend_Threaded::IsIdle() const
void CGraphicsBackend_Threaded::WaitForIdle()
{
std::unique_lock<std::mutex> Lock(m_BufferSwapMutex);
while(m_pBuffer != nullptr)
m_BufferDoneCond.wait(Lock);
m_BufferSwapCond.wait(Lock, [this]() { return m_pBuffer == nullptr; });
}
// ------------ CCommandProcessorFragment_General
@ -157,22 +169,26 @@ void CCommandProcessorFragment_SDL::Cmd_Init(const SCommand_Init *pCommand)
{
m_GLContext = pCommand->m_GLContext;
m_pWindow = pCommand->m_pWindow;
SDL_GL_MakeCurrent(m_pWindow, m_GLContext);
if(m_GLContext)
SDL_GL_MakeCurrent(m_pWindow, m_GLContext);
}
void CCommandProcessorFragment_SDL::Cmd_Shutdown(const SCommand_Shutdown *pCommand)
{
SDL_GL_MakeCurrent(NULL, NULL);
if(m_GLContext)
SDL_GL_MakeCurrent(NULL, NULL);
}
void CCommandProcessorFragment_SDL::Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand)
{
SDL_GL_SwapWindow(m_pWindow);
if(m_GLContext)
SDL_GL_SwapWindow(m_pWindow);
}
void CCommandProcessorFragment_SDL::Cmd_VSync(const CCommandBuffer::SCommand_VSync *pCommand)
{
*pCommand->m_pRetOk = SDL_GL_SetSwapInterval(pCommand->m_VSync) == 0;
if(m_GLContext)
*pCommand->m_pRetOk = SDL_GL_SetSwapInterval(pCommand->m_VSync) == 0;
}
void CCommandProcessorFragment_SDL::Cmd_WindowCreateNtf(const CCommandBuffer::SCommand_WindowCreateNtf *pCommand)
@ -181,7 +197,8 @@ void CCommandProcessorFragment_SDL::Cmd_WindowCreateNtf(const CCommandBuffer::SC
// Android destroys windows when they are not visible, so we get the new one and work with that
// The graphic context does not need to be recreated, just unbound see @see SCommand_WindowDestroyNtf
#ifdef CONF_PLATFORM_ANDROID
SDL_GL_MakeCurrent(m_pWindow, m_GLContext);
if(m_GLContext)
SDL_GL_MakeCurrent(m_pWindow, m_GLContext);
dbg_msg("gfx", "render surface created.");
#endif
}
@ -191,7 +208,8 @@ void CCommandProcessorFragment_SDL::Cmd_WindowDestroyNtf(const CCommandBuffer::S
// Unbind the graphic context from the window, so it does not get destroyed
#ifdef CONF_PLATFORM_ANDROID
dbg_msg("gfx", "render surface destroyed.");
SDL_GL_MakeCurrent(NULL, NULL);
if(m_GLContext)
SDL_GL_MakeCurrent(NULL, NULL);
#endif
}
@ -207,19 +225,23 @@ bool CCommandProcessorFragment_SDL::RunCommand(const CCommandBuffer::SCommand *p
case CCommandBuffer::CMD_VSYNC: Cmd_VSync(static_cast<const CCommandBuffer::SCommand_VSync *>(pBaseCommand)); break;
case CMD_INIT: Cmd_Init(static_cast<const SCommand_Init *>(pBaseCommand)); break;
case CMD_SHUTDOWN: Cmd_Shutdown(static_cast<const SCommand_Shutdown *>(pBaseCommand)); break;
case CCommandProcessorFragment_GLBase::CMD_PRE_INIT: break;
case CCommandProcessorFragment_GLBase::CMD_POST_SHUTDOWN: break;
default: return false;
}
return true;
}
// ------------ CCommandProcessor_SDL_OpenGL
// ------------ CCommandProcessor_SDL_GL
void CCommandProcessor_SDL_OpenGL::RunBuffer(CCommandBuffer *pBuffer)
void CCommandProcessor_SDL_GL::RunBuffer(CCommandBuffer *pBuffer)
{
m_pGLBackend->StartCommands(pBuffer->m_CommandCount, pBuffer->m_RenderCallCount);
for(CCommandBuffer::SCommand *pCommand = pBuffer->Head(); pCommand; pCommand = pCommand->m_pNext)
{
if(m_pOpenGL->RunCommand(pCommand))
if(m_pGLBackend->RunCommand(pCommand))
continue;
if(m_SDL.RunCommand(pCommand))
@ -230,61 +252,69 @@ void CCommandProcessor_SDL_OpenGL::RunBuffer(CCommandBuffer *pBuffer)
dbg_msg("gfx", "unknown command %d", pCommand->m_Cmd);
}
m_pGLBackend->EndCommands();
}
CCommandProcessor_SDL_OpenGL::CCommandProcessor_SDL_OpenGL(EBackendType BackendType, int OpenGLMajor, int OpenGLMinor, int OpenGLPatch)
CCommandProcessor_SDL_GL::CCommandProcessor_SDL_GL(EBackendType BackendType, int GLMajor, int GLMinor, int GLPatch)
{
m_BackendType = BackendType;
if(BackendType == BACKEND_TYPE_OPENGL_ES)
{
#if defined(CONF_BACKEND_OPENGL_ES) || defined(CONF_BACKEND_OPENGL_ES3)
if(OpenGLMajor < 3)
if(GLMajor < 3)
{
m_pOpenGL = new CCommandProcessorFragment_OpenGLES();
m_pGLBackend = new CCommandProcessorFragment_OpenGLES();
}
else
{
m_pOpenGL = new CCommandProcessorFragment_OpenGLES3();
m_pGLBackend = new CCommandProcessorFragment_OpenGLES3();
}
#endif
}
else if(BackendType == BACKEND_TYPE_OPENGL)
{
#if !defined(CONF_BACKEND_OPENGL_ES)
if(OpenGLMajor < 2)
if(GLMajor < 2)
{
m_pOpenGL = new CCommandProcessorFragment_OpenGL();
m_pGLBackend = new CCommandProcessorFragment_OpenGL();
}
if(OpenGLMajor == 2)
if(GLMajor == 2)
{
m_pOpenGL = new CCommandProcessorFragment_OpenGL2();
m_pGLBackend = new CCommandProcessorFragment_OpenGL2();
}
if(OpenGLMajor == 3 && OpenGLMinor == 0)
if(GLMajor == 3 && GLMinor == 0)
{
m_pOpenGL = new CCommandProcessorFragment_OpenGL3();
m_pGLBackend = new CCommandProcessorFragment_OpenGL3();
}
else if((OpenGLMajor == 3 && OpenGLMinor == 3) || OpenGLMajor >= 4)
else if((GLMajor == 3 && GLMinor == 3) || GLMajor >= 4)
{
m_pOpenGL = new CCommandProcessorFragment_OpenGL3_3();
m_pGLBackend = new CCommandProcessorFragment_OpenGL3_3();
}
#endif
}
else if(BackendType == BACKEND_TYPE_VULKAN)
{
#if defined(CONF_BACKEND_VULKAN)
m_pGLBackend = CreateVulkanCommandProcessorFragment();
#endif
}
}
CCommandProcessor_SDL_OpenGL::~CCommandProcessor_SDL_OpenGL()
CCommandProcessor_SDL_GL::~CCommandProcessor_SDL_GL()
{
delete m_pOpenGL;
delete m_pGLBackend;
}
// ------------ CGraphicsBackend_SDL_OpenGL
// ------------ CGraphicsBackend_SDL_GL
static bool BackendInitGlew(EBackendType BackendType, int &GlewMajor, int &GlewMinor, int &GlewPatch)
{
if(BackendType == BACKEND_TYPE_OPENGL)
{
#ifndef CONF_BACKEND_OPENGL_ES
//support graphic cards that are pretty old(and linux)
// support graphic cards that are pretty old(and linux)
glewExperimental = GL_TRUE;
#ifdef CONF_GLEW_HAS_CONTEXT_INIT
if(GLEW_OK != glewContextInit())
@ -529,124 +559,182 @@ static int IsVersionSupportedGlew(EBackendType BackendType, int VersionMajor, in
return InitError;
}
EBackendType CGraphicsBackend_SDL_OpenGL::DetectBackend()
EBackendType CGraphicsBackend_SDL_GL::DetectBackend()
{
#ifndef CONF_BACKEND_OPENGL_ES
#ifdef CONF_BACKEND_OPENGL_ES3
const char *pEnvDriver = getenv("DDNET_DRIVER");
if(pEnvDriver && str_comp(pEnvDriver, "GLES") == 0)
return BACKEND_TYPE_OPENGL_ES;
else
return BACKEND_TYPE_OPENGL;
#else
return BACKEND_TYPE_OPENGL;
EBackendType RetBackendType = BACKEND_TYPE_OPENGL;
const char *pEnvDriver = SDL_getenv("DDNET_DRIVER");
if(pEnvDriver && str_comp_nocase(pEnvDriver, "GLES") == 0)
RetBackendType = BACKEND_TYPE_OPENGL_ES;
else if(pEnvDriver && str_comp_nocase(pEnvDriver, "Vulkan") == 0)
RetBackendType = BACKEND_TYPE_VULKAN;
else if(pEnvDriver && str_comp_nocase(pEnvDriver, "OpenGL") == 0)
RetBackendType = BACKEND_TYPE_OPENGL;
else if(pEnvDriver == nullptr)
{
// load the config backend
const char *pConfBackend = g_Config.m_GfxBackend;
if(str_comp_nocase(pConfBackend, "GLES") == 0)
RetBackendType = BACKEND_TYPE_OPENGL_ES;
else if(str_comp_nocase(pConfBackend, "Vulkan") == 0)
RetBackendType = BACKEND_TYPE_VULKAN;
else if(str_comp_nocase(pConfBackend, "OpenGL") == 0)
RetBackendType = BACKEND_TYPE_OPENGL;
}
#if !defined(CONF_BACKEND_VULKAN)
RetBackendType = BACKEND_TYPE_OPENGL;
#endif
#else
return BACKEND_TYPE_OPENGL_ES;
#if !defined(CONF_BACKEND_OPENGL_ES) && !defined(CONF_BACKEND_OPENGL_ES3)
if(RetBackendType == BACKEND_TYPE_OPENGL_ES)
RetBackendType = BACKEND_TYPE_OPENGL;
#elif defined(CONF_BACKEND_OPENGL_ES)
if(RetBackendType == BACKEND_TYPE_OPENGL)
RetBackendType = BACKEND_TYPE_OPENGL_ES;
#endif
return RetBackendType;
}
void CGraphicsBackend_SDL_OpenGL::ClampDriverVersion(EBackendType BackendType)
void CGraphicsBackend_SDL_GL::ClampDriverVersion(EBackendType BackendType)
{
if(BackendType == BACKEND_TYPE_OPENGL)
{
//clamp the versions to existing versions(only for OpenGL major <= 3)
if(g_Config.m_GfxOpenGLMajor == 1)
// clamp the versions to existing versions(only for OpenGL major <= 3)
if(g_Config.m_GfxGLMajor == 1)
{
g_Config.m_GfxOpenGLMinor = clamp(g_Config.m_GfxOpenGLMinor, 1, 5);
if(g_Config.m_GfxOpenGLMinor == 2)
g_Config.m_GfxOpenGLPatch = clamp(g_Config.m_GfxOpenGLPatch, 0, 1);
g_Config.m_GfxGLMinor = clamp(g_Config.m_GfxGLMinor, 1, 5);
if(g_Config.m_GfxGLMinor == 2)
g_Config.m_GfxGLPatch = clamp(g_Config.m_GfxGLPatch, 0, 1);
else
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 2)
else if(g_Config.m_GfxGLMajor == 2)
{
g_Config.m_GfxOpenGLMinor = clamp(g_Config.m_GfxOpenGLMinor, 0, 1);
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMinor = clamp(g_Config.m_GfxGLMinor, 0, 1);
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 3)
else if(g_Config.m_GfxGLMajor == 3)
{
g_Config.m_GfxOpenGLMinor = clamp(g_Config.m_GfxOpenGLMinor, 0, 3);
if(g_Config.m_GfxOpenGLMinor < 3)
g_Config.m_GfxOpenGLMinor = 0;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMinor = clamp(g_Config.m_GfxGLMinor, 0, 3);
if(g_Config.m_GfxGLMinor < 3)
g_Config.m_GfxGLMinor = 0;
g_Config.m_GfxGLPatch = 0;
}
}
else if(BackendType == BACKEND_TYPE_OPENGL_ES)
{
#if !defined(CONF_BACKEND_OPENGL_ES3)
// Make sure GLES is set to 1.0 (which is equivalent to OpenGL 1.3), if its not set to >= 3.0(which is equivalent to OpenGL 3.3)
if(g_Config.m_GfxOpenGLMajor < 3)
if(g_Config.m_GfxGLMajor < 3)
{
g_Config.m_GfxOpenGLMajor = 1;
g_Config.m_GfxOpenGLMinor = 0;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 1;
g_Config.m_GfxGLMinor = 0;
g_Config.m_GfxGLPatch = 0;
// GLES also doesnt know GL_QUAD
g_Config.m_GfxQuadAsTriangle = 1;
}
#else
g_Config.m_GfxOpenGLMajor = 3;
g_Config.m_GfxOpenGLMinor = 0;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 3;
g_Config.m_GfxGLMinor = 0;
g_Config.m_GfxGLPatch = 0;
#endif
}
else if(BackendType == BACKEND_TYPE_VULKAN)
{
#if defined(CONF_BACKEND_VULKAN)
g_Config.m_GfxGLMajor = gs_BackendVulkanMajor;
g_Config.m_GfxGLMinor = gs_BackendVulkanMinor;
g_Config.m_GfxGLPatch = 0;
#endif
}
}
bool CGraphicsBackend_SDL_OpenGL::IsModernAPI(EBackendType BackendType)
bool CGraphicsBackend_SDL_GL::IsModernAPI(EBackendType BackendType)
{
if(BackendType == BACKEND_TYPE_OPENGL)
return (g_Config.m_GfxOpenGLMajor == 3 && g_Config.m_GfxOpenGLMinor == 3) || g_Config.m_GfxOpenGLMajor >= 4;
return (g_Config.m_GfxGLMajor == 3 && g_Config.m_GfxGLMinor == 3) || g_Config.m_GfxGLMajor >= 4;
else if(BackendType == BACKEND_TYPE_OPENGL_ES)
return g_Config.m_GfxOpenGLMajor >= 3;
return g_Config.m_GfxGLMajor >= 3;
else if(BackendType == BACKEND_TYPE_VULKAN)
return true;
return false;
}
void CGraphicsBackend_SDL_OpenGL::GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch)
bool CGraphicsBackend_SDL_GL::GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch, const char *&pName, EBackendType BackendType)
{
if(m_BackendType == BACKEND_TYPE_OPENGL)
if(BackendType == BACKEND_TYPE_AUTO)
BackendType = m_BackendType;
if(BackendType == BACKEND_TYPE_OPENGL)
{
pName = "OpenGL";
if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_LEGACY)
{
Major = 1;
Minor = 4;
Patch = 0;
return true;
}
else if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_DEFAULT)
{
Major = 3;
Minor = 0;
Patch = 0;
return true;
}
else if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_MODERN)
{
Major = 3;
Minor = 3;
Patch = 0;
return true;
}
}
else if(m_BackendType == BACKEND_TYPE_OPENGL_ES)
else if(BackendType == BACKEND_TYPE_OPENGL_ES)
{
pName = "GLES";
#ifdef CONF_BACKEND_OPENGL_ES
if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_LEGACY)
{
Major = 1;
Minor = 0;
Patch = 0;
return true;
}
else if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_DEFAULT)
{
Major = 3;
Minor = 0;
Patch = 0;
// there isn't really a default one
return false;
}
else if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_MODERN)
#endif
#ifdef CONF_BACKEND_OPENGL_ES3
if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_MODERN)
{
Major = 3;
Minor = 0;
Patch = 0;
return true;
}
#endif
}
else if(BackendType == BACKEND_TYPE_VULKAN)
{
pName = "Vulkan";
#ifdef CONF_BACKEND_VULKAN
if(DriverAgeType == GRAPHICS_DRIVER_AGE_TYPE_DEFAULT)
{
Major = gs_BackendVulkanMajor;
Minor = gs_BackendVulkanMinor;
Patch = 0;
return true;
}
#else
return false;
#endif
}
return false;
}
static void DisplayToVideoMode(CVideoMode *pVMode, SDL_DisplayMode *pMode, int HiDPIScale, int RefreshRate)
@ -662,7 +750,7 @@ static void DisplayToVideoMode(CVideoMode *pVMode, SDL_DisplayMode *pMode, int H
pVMode->m_Format = pMode->format;
}
void CGraphicsBackend_SDL_OpenGL::GetVideoModes(CVideoMode *pModes, int MaxModes, int *pNumModes, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int Screen)
void CGraphicsBackend_SDL_GL::GetVideoModes(CVideoMode *pModes, int MaxModes, int *pNumModes, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int Screen)
{
SDL_DisplayMode DesktopMode;
int maxModes = SDL_GetNumDisplayModes(Screen);
@ -726,7 +814,7 @@ void CGraphicsBackend_SDL_OpenGL::GetVideoModes(CVideoMode *pModes, int MaxModes
*pNumModes = numModes;
}
void CGraphicsBackend_SDL_OpenGL::GetCurrentVideoMode(CVideoMode &CurMode, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int Screen)
void CGraphicsBackend_SDL_GL::GetCurrentVideoMode(CVideoMode &CurMode, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int Screen)
{
SDL_DisplayMode DPMode;
// if "real" fullscreen, obtain the video mode for that
@ -755,12 +843,12 @@ void CGraphicsBackend_SDL_OpenGL::GetCurrentVideoMode(CVideoMode &CurMode, int H
DisplayToVideoMode(&CurMode, &DPMode, HiDPIScale, DPMode.refresh_rate);
}
CGraphicsBackend_SDL_OpenGL::CGraphicsBackend_SDL_OpenGL()
CGraphicsBackend_SDL_GL::CGraphicsBackend_SDL_GL()
{
mem_zero(m_aErrorString, sizeof(m_aErrorString) / sizeof(m_aErrorString[0]));
}
int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWidth, int *pHeight, int *pRefreshRate, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, IStorage *pStorage)
int CGraphicsBackend_SDL_GL::Init(const char *pName, int *pScreen, int *pWidth, int *pHeight, int *pRefreshRate, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, IStorage *pStorage)
{
// print sdl version
{
@ -782,23 +870,39 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
}
}
EBackendType OldBackendType = m_BackendType;
m_BackendType = DetectBackend();
// little fallback for Vulkan
if(OldBackendType != BACKEND_TYPE_AUTO)
{
if(m_BackendType == BACKEND_TYPE_VULKAN)
{
SDL_setenv("DDNET_DRIVER", "OpenGL", 1);
m_BackendType = DetectBackend();
}
}
ClampDriverVersion(m_BackendType);
m_UseNewOpenGL = IsModernAPI(m_BackendType);
bool UseModernGL = IsModernAPI(m_BackendType);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, g_Config.m_GfxOpenGLMajor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, g_Config.m_GfxOpenGLMinor);
dbg_msg("gfx", "Created OpenGL %d.%d context.", g_Config.m_GfxOpenGLMajor, g_Config.m_GfxOpenGLMinor);
bool IsOpenGLFamilyBackend = m_BackendType == BACKEND_TYPE_OPENGL || m_BackendType == BACKEND_TYPE_OPENGL_ES;
if(IsOpenGLFamilyBackend)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, g_Config.m_GfxGLMajor);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, g_Config.m_GfxGLMinor);
}
dbg_msg("gfx", "Created %s %d.%d context.", ((m_BackendType == BACKEND_TYPE_VULKAN) ? "Vulkan" : "OpenGL"), g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor);
if(m_BackendType == BACKEND_TYPE_OPENGL)
{
if(g_Config.m_GfxOpenGLMajor == 3 && g_Config.m_GfxOpenGLMinor == 0)
if(g_Config.m_GfxGLMajor == 3 && g_Config.m_GfxGLMinor == 0)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
}
else if(m_UseNewOpenGL)
else if(UseModernGL)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
}
@ -858,7 +962,8 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
}
// set flags
int SdlFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_INPUT_GRABBED | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS;
int SdlFlags = SDL_WINDOW_INPUT_GRABBED | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS;
SdlFlags |= (IsOpenGLFamilyBackend) ? SDL_WINDOW_OPENGL : SDL_WINDOW_VULKAN;
if(Flags & IGraphicsBackend::INITFLAG_HIGHDPI)
SdlFlags |= SDL_WINDOW_ALLOW_HIGHDPI;
if(Flags & IGraphicsBackend::INITFLAG_RESIZABLE)
@ -891,16 +996,19 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
}
// set gl attributes
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
if(FsaaSamples)
if(IsOpenGLFamilyBackend)
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, FsaaSamples);
}
else
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
if(FsaaSamples)
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, FsaaSamples);
}
else
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
}
}
if(g_Config.m_InpMouseOld)
@ -918,63 +1026,86 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
if(m_pWindow == NULL)
{
dbg_msg("gfx", "unable to create window: %s", SDL_GetError());
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_SDL_WINDOW_CREATE_FAILED;
}
m_GLContext = SDL_GL_CreateContext(m_pWindow);
if(m_GLContext == NULL)
{
SDL_DestroyWindow(m_pWindow);
dbg_msg("gfx", "unable to create OpenGL context: %s", SDL_GetError());
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_OPENGL_CONTEXT_FAILED;
if(m_BackendType == BACKEND_TYPE_VULKAN)
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED;
else
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_SDL_WINDOW_CREATE_FAILED;
}
int GlewMajor = 0;
int GlewMinor = 0;
int GlewPatch = 0;
if(!BackendInitGlew(m_BackendType, GlewMajor, GlewMinor, GlewPatch))
if(IsOpenGLFamilyBackend)
{
SDL_GL_DeleteContext(m_GLContext);
SDL_DestroyWindow(m_pWindow);
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_UNKNOWN;
m_GLContext = SDL_GL_CreateContext(m_pWindow);
if(m_GLContext == NULL)
{
SDL_DestroyWindow(m_pWindow);
dbg_msg("gfx", "unable to create graphic context: %s", SDL_GetError());
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED;
}
if(!BackendInitGlew(m_BackendType, GlewMajor, GlewMinor, GlewPatch))
{
SDL_GL_DeleteContext(m_GLContext);
SDL_DestroyWindow(m_pWindow);
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_UNKNOWN;
}
}
int InitError = 0;
const char *pErrorStr = NULL;
InitError = IsVersionSupportedGlew(m_BackendType, g_Config.m_GfxOpenGLMajor, g_Config.m_GfxOpenGLMinor, g_Config.m_GfxOpenGLPatch, GlewMajor, GlewMinor, GlewPatch);
InitError = IsVersionSupportedGlew(m_BackendType, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch, GlewMajor, GlewMinor, GlewPatch);
// SDL_GL_GetDrawableSize reports HiDPI resolution even with SDL_WINDOW_ALLOW_HIGHDPI not set, which is wrong
if(SdlFlags & SDL_WINDOW_ALLOW_HIGHDPI)
if(SdlFlags & SDL_WINDOW_ALLOW_HIGHDPI && IsOpenGLFamilyBackend)
SDL_GL_GetDrawableSize(m_pWindow, pCurrentWidth, pCurrentHeight);
else
SDL_GetWindowSize(m_pWindow, pCurrentWidth, pCurrentHeight);
SDL_GL_SetSwapInterval(Flags & IGraphicsBackend::INITFLAG_VSYNC ? 1 : 0);
SDL_GL_MakeCurrent(NULL, NULL);
if(IsOpenGLFamilyBackend)
{
SDL_GL_SetSwapInterval(Flags & IGraphicsBackend::INITFLAG_VSYNC ? 1 : 0);
SDL_GL_MakeCurrent(NULL, NULL);
}
if(InitError != 0)
{
SDL_GL_DeleteContext(m_GLContext);
if(m_GLContext)
SDL_GL_DeleteContext(m_GLContext);
SDL_DestroyWindow(m_pWindow);
// try setting to glew supported version
g_Config.m_GfxOpenGLMajor = GlewMajor;
g_Config.m_GfxOpenGLMinor = GlewMinor;
g_Config.m_GfxOpenGLPatch = GlewPatch;
g_Config.m_GfxGLMajor = GlewMajor;
g_Config.m_GfxGLMinor = GlewMinor;
g_Config.m_GfxGLPatch = GlewPatch;
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_OPENGL_VERSION_FAILED;
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_VERSION_FAILED;
}
// start the command processor
m_pProcessor = new CCommandProcessor_SDL_OpenGL(m_BackendType, g_Config.m_GfxOpenGLMajor, g_Config.m_GfxOpenGLMinor, g_Config.m_GfxOpenGLPatch);
dbg_assert(m_pProcessor == nullptr, "Processor was not cleaned up properly.");
m_pProcessor = new CCommandProcessor_SDL_GL(m_BackendType, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch);
StartProcessor(m_pProcessor);
// issue init commands for OpenGL and SDL
CCommandBuffer CmdBuffer(1024, 512);
//run sdl first to have the context in the thread
CCommandProcessorFragment_GLBase::SCommand_PreInit CmdPre;
CmdPre.m_pWindow = m_pWindow;
CmdPre.m_Width = *pCurrentWidth;
CmdPre.m_Height = *pCurrentHeight;
CmdPre.m_pVendorString = m_aVendorString;
CmdPre.m_pVersionString = m_aVersionString;
CmdPre.m_pRendererString = m_aRendererString;
CmdPre.m_pGPUList = &m_GPUList;
CmdBuffer.AddCommandUnsafe(CmdPre);
RunBufferSingleThreadedUnsafe(&CmdBuffer);
CmdBuffer.Reset();
// run sdl first to have the context in the thread
CCommandProcessorFragment_SDL::SCommand_Init CmdSDL;
CmdSDL.m_pWindow = m_pWindow;
CmdSDL.m_GLContext = m_GLContext;
@ -985,23 +1116,31 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
if(InitError == 0)
{
CCommandProcessorFragment_OpenGLBase::SCommand_Init CmdOpenGL;
CmdOpenGL.m_pTextureMemoryUsage = &m_TextureMemoryUsage;
CmdOpenGL.m_pStorage = pStorage;
CmdOpenGL.m_pCapabilities = &m_Capabilites;
CmdOpenGL.m_pInitError = &InitError;
CmdOpenGL.m_RequestedMajor = g_Config.m_GfxOpenGLMajor;
CmdOpenGL.m_RequestedMinor = g_Config.m_GfxOpenGLMinor;
CmdOpenGL.m_RequestedPatch = g_Config.m_GfxOpenGLPatch;
CmdOpenGL.m_GlewMajor = GlewMajor;
CmdOpenGL.m_GlewMinor = GlewMinor;
CmdOpenGL.m_GlewPatch = GlewPatch;
CmdOpenGL.m_pErrStringPtr = &pErrorStr;
CmdOpenGL.m_pVendorString = m_aVendorString;
CmdOpenGL.m_pVersionString = m_aVersionString;
CmdOpenGL.m_pRendererString = m_aRendererString;
CmdOpenGL.m_RequestedBackend = m_BackendType;
CmdBuffer.AddCommandUnsafe(CmdOpenGL);
CCommandProcessorFragment_GLBase::SCommand_Init CmdGL;
CmdGL.m_pWindow = m_pWindow;
CmdGL.m_Width = *pCurrentWidth;
CmdGL.m_Height = *pCurrentHeight;
CmdGL.m_pTextureMemoryUsage = &m_TextureMemoryUsage;
CmdGL.m_pBufferMemoryUsage = &m_BufferMemoryUsage;
CmdGL.m_pStreamMemoryUsage = &m_StreamMemoryUsage;
CmdGL.m_pStagingMemoryUsage = &m_StagingMemoryUsage;
CmdGL.m_pGPUList = &m_GPUList;
CmdGL.m_pReadPresentedImageDataFunc = &m_ReadPresentedImageDataFunc;
CmdGL.m_pStorage = pStorage;
CmdGL.m_pCapabilities = &m_Capabilites;
CmdGL.m_pInitError = &InitError;
CmdGL.m_RequestedMajor = g_Config.m_GfxGLMajor;
CmdGL.m_RequestedMinor = g_Config.m_GfxGLMinor;
CmdGL.m_RequestedPatch = g_Config.m_GfxGLPatch;
CmdGL.m_GlewMajor = GlewMajor;
CmdGL.m_GlewMinor = GlewMinor;
CmdGL.m_GlewPatch = GlewPatch;
CmdGL.m_pErrStringPtr = &pErrorStr;
CmdGL.m_pVendorString = m_aVendorString;
CmdGL.m_pVersionString = m_aVersionString;
CmdGL.m_pRendererString = m_aRendererString;
CmdGL.m_RequestedBackend = m_BackendType;
CmdBuffer.AddCommandUnsafe(CmdGL);
RunBuffer(&CmdBuffer);
WaitForIdle();
@ -1013,7 +1152,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
if(InitError != -2)
{
// shutdown the context, as it might have been initialized
CCommandProcessorFragment_OpenGLBase::SCommand_Shutdown CmdGL;
CCommandProcessorFragment_GLBase::SCommand_Shutdown CmdGL;
CmdBuffer.AddCommandUnsafe(CmdGL);
RunBuffer(&CmdBuffer);
WaitForIdle();
@ -1026,20 +1165,26 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
WaitForIdle();
CmdBuffer.Reset();
CCommandProcessorFragment_GLBase::SCommand_PostShutdown CmdPost;
CmdBuffer.AddCommandUnsafe(CmdPost);
RunBufferSingleThreadedUnsafe(&CmdBuffer);
CmdBuffer.Reset();
// stop and delete the processor
StopProcessor();
delete m_pProcessor;
m_pProcessor = 0;
m_pProcessor = nullptr;
SDL_GL_DeleteContext(m_GLContext);
if(m_GLContext)
SDL_GL_DeleteContext(m_GLContext);
SDL_DestroyWindow(m_pWindow);
// try setting to version string's supported version
if(InitError == -2)
{
g_Config.m_GfxOpenGLMajor = m_Capabilites.m_ContextMajor;
g_Config.m_GfxOpenGLMinor = m_Capabilites.m_ContextMinor;
g_Config.m_GfxOpenGLPatch = m_Capabilites.m_ContextPatch;
g_Config.m_GfxGLMajor = m_Capabilites.m_ContextMajor;
g_Config.m_GfxGLMinor = m_Capabilites.m_ContextMinor;
g_Config.m_GfxGLPatch = m_Capabilites.m_ContextPatch;
}
if(pErrorStr != NULL)
@ -1047,7 +1192,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
str_copy(m_aErrorString, pErrorStr, sizeof(m_aErrorString) / sizeof(m_aErrorString[0]));
}
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_OPENGL_VERSION_FAILED;
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_VERSION_FAILED;
}
{
@ -1057,6 +1202,7 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
CmdSDL2.m_Width = *pCurrentWidth;
CmdSDL2.m_Height = *pCurrentHeight;
CmdSDL2.m_ByResize = true;
CmdBuffer.AddCommandUnsafe(CmdSDL2);
RunBuffer(&CmdBuffer);
WaitForIdle();
@ -1067,11 +1213,11 @@ int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int *pScreen, int *pWid
return EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_NONE;
}
int CGraphicsBackend_SDL_OpenGL::Shutdown()
int CGraphicsBackend_SDL_GL::Shutdown()
{
// issue a shutdown command
CCommandBuffer CmdBuffer(1024, 512);
CCommandProcessorFragment_OpenGLBase::SCommand_Shutdown CmdGL;
CCommandProcessorFragment_GLBase::SCommand_Shutdown CmdGL;
CmdBuffer.AddCommandUnsafe(CmdGL);
RunBuffer(&CmdBuffer);
WaitForIdle();
@ -1083,10 +1229,15 @@ int CGraphicsBackend_SDL_OpenGL::Shutdown()
WaitForIdle();
CmdBuffer.Reset();
CCommandProcessorFragment_GLBase::SCommand_PostShutdown CmdPost;
CmdBuffer.AddCommandUnsafe(CmdPost);
RunBufferSingleThreadedUnsafe(&CmdBuffer);
CmdBuffer.Reset();
// stop and delete the processor
StopProcessor();
delete m_pProcessor;
m_pProcessor = 0;
m_pProcessor = nullptr;
SDL_GL_DeleteContext(m_GLContext);
SDL_DestroyWindow(m_pWindow);
@ -1095,22 +1246,42 @@ int CGraphicsBackend_SDL_OpenGL::Shutdown()
return 0;
}
int CGraphicsBackend_SDL_OpenGL::MemoryUsage() const
uint64_t CGraphicsBackend_SDL_GL::TextureMemoryUsage() const
{
return m_TextureMemoryUsage;
}
void CGraphicsBackend_SDL_OpenGL::Minimize()
uint64_t CGraphicsBackend_SDL_GL::BufferMemoryUsage() const
{
return m_BufferMemoryUsage;
}
uint64_t CGraphicsBackend_SDL_GL::StreamedMemoryUsage() const
{
return m_StreamMemoryUsage;
}
uint64_t CGraphicsBackend_SDL_GL::StagingMemoryUsage() const
{
return m_StagingMemoryUsage;
}
const TTWGraphicsGPUList &CGraphicsBackend_SDL_GL::GetGPUs() const
{
return m_GPUList;
}
void CGraphicsBackend_SDL_GL::Minimize()
{
SDL_MinimizeWindow(m_pWindow);
}
void CGraphicsBackend_SDL_OpenGL::Maximize()
void CGraphicsBackend_SDL_GL::Maximize()
{
// TODO: SDL
}
void CGraphicsBackend_SDL_OpenGL::SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing)
void CGraphicsBackend_SDL_GL::SetWindowParams(int FullscreenMode, bool IsBorderless, bool AllowResizing)
{
if(FullscreenMode > 0)
{
@ -1159,7 +1330,7 @@ void CGraphicsBackend_SDL_OpenGL::SetWindowParams(int FullscreenMode, bool IsBor
}
}
bool CGraphicsBackend_SDL_OpenGL::SetWindowScreen(int Index)
bool CGraphicsBackend_SDL_GL::SetWindowScreen(int Index)
{
if(Index < 0 || Index >= m_NumScreens)
{
@ -1179,7 +1350,7 @@ bool CGraphicsBackend_SDL_OpenGL::SetWindowScreen(int Index)
return UpdateDisplayMode(Index);
}
bool CGraphicsBackend_SDL_OpenGL::UpdateDisplayMode(int Index)
bool CGraphicsBackend_SDL_GL::UpdateDisplayMode(int Index)
{
SDL_DisplayMode DisplayMode;
if(SDL_GetDesktopDisplayMode(Index, &DisplayMode) < 0)
@ -1194,27 +1365,27 @@ bool CGraphicsBackend_SDL_OpenGL::UpdateDisplayMode(int Index)
return true;
}
int CGraphicsBackend_SDL_OpenGL::GetWindowScreen()
int CGraphicsBackend_SDL_GL::GetWindowScreen()
{
return SDL_GetWindowDisplayIndex(m_pWindow);
}
int CGraphicsBackend_SDL_OpenGL::WindowActive()
int CGraphicsBackend_SDL_GL::WindowActive()
{
return m_pWindow && SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_INPUT_FOCUS;
}
int CGraphicsBackend_SDL_OpenGL::WindowOpen()
int CGraphicsBackend_SDL_GL::WindowOpen()
{
return m_pWindow && SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_SHOWN;
}
void CGraphicsBackend_SDL_OpenGL::SetWindowGrab(bool Grab)
void CGraphicsBackend_SDL_GL::SetWindowGrab(bool Grab)
{
SDL_SetWindowGrab(m_pWindow, Grab ? SDL_TRUE : SDL_FALSE);
}
bool CGraphicsBackend_SDL_OpenGL::ResizeWindow(int w, int h, int RefreshRate)
bool CGraphicsBackend_SDL_GL::ResizeWindow(int w, int h, int RefreshRate)
{
// don't call resize events when the window is at fullscreen desktop
if(!m_pWindow || (SDL_GetWindowFlags(m_pWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
@ -1252,12 +1423,12 @@ bool CGraphicsBackend_SDL_OpenGL::ResizeWindow(int w, int h, int RefreshRate)
return false;
}
void CGraphicsBackend_SDL_OpenGL::GetViewportSize(int &w, int &h)
void CGraphicsBackend_SDL_GL::GetViewportSize(int &w, int &h)
{
SDL_GL_GetDrawableSize(m_pWindow, &w, &h);
}
void CGraphicsBackend_SDL_OpenGL::NotifyWindow()
void CGraphicsBackend_SDL_GL::NotifyWindow()
{
#if SDL_MAJOR_VERSION > 2 || (SDL_MAJOR_VERSION == 2 && SDL_PATCHLEVEL >= 16)
if(SDL_FlashWindow(m_pWindow, SDL_FlashOperation::SDL_FLASH_UNTIL_FOCUSED) != 0)
@ -1268,13 +1439,18 @@ void CGraphicsBackend_SDL_OpenGL::NotifyWindow()
#endif
}
void CGraphicsBackend_SDL_OpenGL::WindowDestroyNtf(uint32_t WindowID)
void CGraphicsBackend_SDL_GL::WindowDestroyNtf(uint32_t WindowID)
{
}
void CGraphicsBackend_SDL_OpenGL::WindowCreateNtf(uint32_t WindowID)
void CGraphicsBackend_SDL_GL::WindowCreateNtf(uint32_t WindowID)
{
m_pWindow = SDL_GetWindowFromID(WindowID);
}
IGraphicsBackend *CreateGraphicsBackend() { return new CGraphicsBackend_SDL_OpenGL; }
TGLBackendReadPresentedImageData &CGraphicsBackend_SDL_GL::GetReadPresentedImageDataFuncUnsafe()
{
return m_ReadPresentedImageDataFunc;
}
IGraphicsBackend *CreateGraphicsBackend() { return new CGraphicsBackend_SDL_GL; }

View file

@ -5,6 +5,7 @@
#include <base/detect.h>
#include "engine/graphics.h"
#include "graphics_defines.h"
#include "blocklist_driver.h"
@ -58,6 +59,7 @@ public:
CGraphicsBackend_Threaded();
virtual void RunBuffer(CCommandBuffer *pBuffer);
virtual void RunBufferSingleThreadedUnsafe(CCommandBuffer *pBuffer);
virtual bool IsIdle() const;
virtual void WaitForIdle();
@ -69,9 +71,8 @@ private:
ICommandProcessor *m_pProcessor;
std::mutex m_BufferSwapMutex;
std::condition_variable m_BufferSwapCond;
std::condition_variable m_BufferDoneCond;
CCommandBuffer *m_pBuffer;
std::atomic_bool m_Shutdown;
std::atomic_bool m_Shutdown = true;
bool m_Started = false;
std::atomic_bool m_BufferInProcess;
void *m_Thread;
@ -89,12 +90,6 @@ public:
bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};
enum EBackendType
{
BACKEND_TYPE_OPENGL = 0,
BACKEND_TYPE_OPENGL_ES,
};
struct SBackendCapabilites
{
bool m_TileBuffering;
@ -109,6 +104,9 @@ struct SBackendCapabilites
bool m_2DArrayTexturesAsExtension;
bool m_ShaderSupport;
// use quads as much as possible, even if the user config says otherwise
bool m_TrianglesAsQuads;
int m_ContextMajor;
int m_ContextMinor;
int m_ContextPatch;
@ -156,75 +154,38 @@ public:
bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};
class CCommandProcessorFragment_OpenGLBase
{
public:
virtual ~CCommandProcessorFragment_OpenGLBase() = default;
virtual bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand) = 0;
enum
{
CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_OPENGL,
CMD_SHUTDOWN = CMD_INIT + 1,
};
struct SCommand_Init : public CCommandBuffer::SCommand
{
SCommand_Init() :
SCommand(CMD_INIT) {}
class IStorage *m_pStorage;
std::atomic<int> *m_pTextureMemoryUsage;
SBackendCapabilites *m_pCapabilities;
int *m_pInitError;
const char **m_pErrStringPtr;
char *m_pVendorString;
char *m_pVersionString;
char *m_pRendererString;
int m_RequestedMajor;
int m_RequestedMinor;
int m_RequestedPatch;
EBackendType m_RequestedBackend;
int m_GlewMajor;
int m_GlewMinor;
int m_GlewPatch;
};
struct SCommand_Shutdown : public CCommandBuffer::SCommand
{
SCommand_Shutdown() :
SCommand(CMD_SHUTDOWN) {}
};
};
// command processor impelementation, uses the fragments to combine into one processor
class CCommandProcessor_SDL_OpenGL : public CGraphicsBackend_Threaded::ICommandProcessor
class CCommandProcessor_SDL_GL : public CGraphicsBackend_Threaded::ICommandProcessor
{
CCommandProcessorFragment_OpenGLBase *m_pOpenGL;
class CCommandProcessorFragment_GLBase *m_pGLBackend;
CCommandProcessorFragment_SDL m_SDL;
CCommandProcessorFragment_General m_General;
EBackendType m_BackendType;
public:
CCommandProcessor_SDL_OpenGL(EBackendType BackendType, int OpenGLMajor, int OpenGLMinor, int OpenGLPatch);
virtual ~CCommandProcessor_SDL_OpenGL();
CCommandProcessor_SDL_GL(EBackendType BackendType, int GLMajor, int GLMinor, int GLPatch);
virtual ~CCommandProcessor_SDL_GL();
virtual void RunBuffer(CCommandBuffer *pBuffer);
};
static constexpr size_t gs_GPUInfoStringSize = 256;
// graphics backend implemented with SDL and OpenGL
class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
// graphics backend implemented with SDL and the graphics library @see EBackendType
class CGraphicsBackend_SDL_GL : public CGraphicsBackend_Threaded
{
SDL_Window *m_pWindow = NULL;
SDL_GLContext m_GLContext;
ICommandProcessor *m_pProcessor;
std::atomic<int> m_TextureMemoryUsage;
ICommandProcessor *m_pProcessor = nullptr;
std::atomic<uint64_t> m_TextureMemoryUsage{0};
std::atomic<uint64_t> m_BufferMemoryUsage{0};
std::atomic<uint64_t> m_StreamMemoryUsage{0};
std::atomic<uint64_t> m_StagingMemoryUsage{0};
TTWGraphicsGPUList m_GPUList;
TGLBackendReadPresentedImageData m_ReadPresentedImageDataFunc;
int m_NumScreens;
SBackendCapabilites m_Capabilites;
@ -233,8 +194,7 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
char m_aVersionString[gs_GPUInfoStringSize] = {};
char m_aRendererString[gs_GPUInfoStringSize] = {};
bool m_UseNewOpenGL;
EBackendType m_BackendType;
EBackendType m_BackendType = BACKEND_TYPE_AUTO;
char m_aErrorString[256];
@ -242,11 +202,16 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
static void ClampDriverVersion(EBackendType BackendType);
public:
CGraphicsBackend_SDL_OpenGL();
CGraphicsBackend_SDL_GL();
virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int *pRefreshRate, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, class IStorage *pStorage);
virtual int Shutdown();
virtual int MemoryUsage() const;
virtual uint64_t TextureMemoryUsage() const;
virtual uint64_t BufferMemoryUsage() const;
virtual uint64_t StreamedMemoryUsage() const;
virtual uint64_t StagingMemoryUsage() const;
virtual const TTWGraphicsGPUList &GetGPUs() const;
virtual int GetNumScreens() const { return m_NumScreens; }
@ -269,9 +234,9 @@ public:
virtual void WindowDestroyNtf(uint32_t WindowID);
virtual void WindowCreateNtf(uint32_t WindowID);
virtual void GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch);
virtual bool GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch, const char *&pName, EBackendType BackendType);
virtual bool IsConfigModernAPI() { return IsModernAPI(m_BackendType); }
virtual bool IsNewOpenGL() { return m_UseNewOpenGL; }
virtual bool UseTrianglesAsQuad() { return m_Capabilites.m_TrianglesAsQuads; }
virtual bool HasTileBuffering() { return m_Capabilites.m_TileBuffering; }
virtual bool HasQuadBuffering() { return m_Capabilites.m_QuadBuffering; }
virtual bool HasTextBuffering() { return m_Capabilites.m_TextBuffering; }
@ -301,6 +266,8 @@ public:
return m_aRendererString;
}
virtual TGLBackendReadPresentedImageData &GetReadPresentedImageDataFuncUnsafe();
static bool IsModernAPI(EBackendType BackendType);
};

View file

@ -1046,9 +1046,12 @@ void CClient::DebugRender()
total = 42
*/
FrameTimeAvg = FrameTimeAvg * 0.9f + m_RenderFrameTime * 0.1f;
str_format(aBuffer, sizeof(aBuffer), "ticks: %8d %8d gfxmem: %dk fps: %3d",
str_format(aBuffer, sizeof(aBuffer), "ticks: %8d %8d gfx mem(tex/buff/stream/staging): (%" PRIu64 "k/%" PRIu64 "k/%" PRIu64 "k/%" PRIu64 "k) fps: %3d",
m_CurGameTick[g_Config.m_ClDummy], m_PredTick[g_Config.m_ClDummy],
Graphics()->MemoryUsage() / 1024,
(Graphics()->TextureMemoryUsage() / 1024),
(Graphics()->BufferMemoryUsage() / 1024),
(Graphics()->StreamedMemoryUsage() / 1024),
(Graphics()->StagingMemoryUsage() / 1024),
(int)(1.0f / FrameTimeAvg + 0.5f));
Graphics()->QuadsText(2, 2, 16, aBuffer);
@ -2482,10 +2485,8 @@ void CClient::Update()
#if defined(CONF_VIDEORECORDER)
if(m_DemoPlayer.IsPlaying() && IVideo::Current())
{
if(IVideo::Current()->FrameRendered())
IVideo::Current()->NextVideoFrame();
if(IVideo::Current()->AudioFrameRendered())
IVideo::Current()->NextAudioFrameTimeline();
IVideo::Current()->NextVideoFrame();
IVideo::Current()->NextAudioFrameTimeline(Sound()->GetSoundMixFunc());
}
else if(m_ButtonRender)
Disconnect();
@ -3096,9 +3097,20 @@ void CClient::Run()
bool AsyncRenderOld = g_Config.m_GfxAsyncRenderOld;
int GfxRefreshRate = g_Config.m_GfxRefreshRate;
#if defined(CONF_VIDEORECORDER)
// keep rendering synced
if(IVideo::Current())
{
AsyncRenderOld = false;
GfxRefreshRate = 0;
}
#endif
if(IsRenderActive &&
(!AsyncRenderOld || m_pGraphics->IsIdle()) &&
(!g_Config.m_GfxRefreshRate || (time_freq() / (int64_t)g_Config.m_GfxRefreshRate) <= Now - LastRenderTime))
(!GfxRefreshRate || (time_freq() / (int64_t)g_Config.m_GfxRefreshRate) <= Now - LastRenderTime))
{
m_RenderFrames++;
@ -3426,7 +3438,12 @@ void CClient::Con_StartVideo(IConsole::IResult *pResult, void *pUserData)
if(!IVideo::Current())
{
new CVideo((CGraphics_Threaded *)pSelf->m_pGraphics, pSelf->Storage(), pSelf->m_pConsole, pSelf->Graphics()->ScreenWidth(), pSelf->Graphics()->ScreenHeight(), "");
// wait for idle, so there is no data race
pSelf->Graphics()->WaitForIdle();
// pause the sound device while creating the video instance
pSelf->Sound()->PauseAudioDevice();
new CVideo((CGraphics_Threaded *)pSelf->m_pGraphics, pSelf->Sound(), pSelf->Storage(), pSelf->m_pConsole, pSelf->Graphics()->ScreenWidth(), pSelf->Graphics()->ScreenHeight(), "");
pSelf->Sound()->UnpauseAudioDevice();
IVideo::Current()->Start();
bool paused = pSelf->m_DemoPlayer.Info()->m_Info.m_Paused;
if(paused)
@ -3446,7 +3463,12 @@ void CClient::StartVideo(IConsole::IResult *pResult, void *pUserData, const char
pSelf->m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "demo_render", pVideoName);
if(!IVideo::Current())
{
new CVideo((CGraphics_Threaded *)pSelf->m_pGraphics, pSelf->Storage(), pSelf->m_pConsole, pSelf->Graphics()->ScreenWidth(), pSelf->Graphics()->ScreenHeight(), pVideoName);
// wait for idle, so there is no data race
pSelf->Graphics()->WaitForIdle();
// pause the sound device while creating the video instance
pSelf->Sound()->PauseAudioDevice();
new CVideo((CGraphics_Threaded *)pSelf->m_pGraphics, pSelf->Sound(), pSelf->Storage(), pSelf->m_pConsole, pSelf->Graphics()->ScreenWidth(), pSelf->Graphics()->ScreenHeight(), pVideoName);
pSelf->Sound()->UnpauseAudioDevice();
IVideo::Current()->Start();
}
else

View file

@ -73,24 +73,6 @@ void CGraphics_Threaded::FlushVertices(bool KeepVertices)
}
}
void CGraphics_Threaded::FlushTextVertices(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor)
{
CCommandBuffer::SCommand_RenderTextStream Cmd;
int PrimType, PrimCount, NumVerts;
size_t VertSize = sizeof(CCommandBuffer::SVertex);
Cmd.m_TextureSize = TextureSize;
Cmd.m_TextTextureIndex = TextTextureIndex;
Cmd.m_TextOutlineTextureIndex = TextOutlineTextureIndex;
mem_copy(Cmd.m_aTextOutlineColor, pOutlineTextColor, sizeof(Cmd.m_aTextOutlineColor));
FlushVerticesImpl(false, PrimType, PrimCount, NumVerts, Cmd, VertSize);
if(Cmd.m_pVertices != NULL)
{
mem_copy(Cmd.m_pVertices, m_aVertices, VertSize * NumVerts);
}
}
void CGraphics_Threaded::FlushVerticesTex3D()
{
CCommandBuffer::SCommand_RenderTex3D Cmd;
@ -209,9 +191,29 @@ void CGraphics_Threaded::WrapClamp()
m_State.m_WrapMode = CCommandBuffer::WRAP_CLAMP;
}
int CGraphics_Threaded::MemoryUsage() const
uint64_t CGraphics_Threaded::TextureMemoryUsage() const
{
return m_pBackend->MemoryUsage();
return m_pBackend->TextureMemoryUsage();
}
uint64_t CGraphics_Threaded::BufferMemoryUsage() const
{
return m_pBackend->BufferMemoryUsage();
}
uint64_t CGraphics_Threaded::StreamedMemoryUsage() const
{
return m_pBackend->StreamedMemoryUsage();
}
uint64_t CGraphics_Threaded::StagingMemoryUsage() const
{
return m_pBackend->StagingMemoryUsage();
}
const TTWGraphicsGPUList &CGraphics_Threaded::GetGPUs() const
{
return m_pBackend->GetGPUs();
}
void CGraphics_Threaded::MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY)
@ -284,27 +286,52 @@ int CGraphics_Threaded::UnloadTexture(CTextureHandle *pIndex)
return 0;
}
static int ImageFormatToTexFormat(int Format)
{
if(Format == CImageInfo::FORMAT_RGB)
return CCommandBuffer::TEXFORMAT_RGB;
if(Format == CImageInfo::FORMAT_RGBA)
return CCommandBuffer::TEXFORMAT_RGBA;
if(Format == CImageInfo::FORMAT_ALPHA)
return CCommandBuffer::TEXFORMAT_ALPHA;
return CCommandBuffer::TEXFORMAT_RGBA;
}
static int ImageFormatToPixelSize(int Format)
{
switch(Format)
{
case CImageInfo::FORMAT_RGB: return 3;
case CImageInfo::FORMAT_ALPHA: return 1;
case CImageInfo::FORMAT_SINGLE_COMPONENT: return 1;
default: return 4;
}
}
static bool ConvertToRGBA(uint8_t *pDest, const uint8_t *pSrc, size_t SrcWidth, size_t SrcHeight, int SrcFormat)
{
if(SrcFormat == CImageInfo::FORMAT_RGBA)
{
mem_copy(pDest, pSrc, SrcWidth * SrcHeight * 4);
return true;
}
else
{
size_t SrcChannelCount = ImageFormatToPixelSize(SrcFormat);
size_t DstChannelCount = 4;
for(size_t Y = 0; Y < SrcHeight; ++Y)
{
for(size_t X = 0; X < SrcHeight; ++X)
{
size_t ImgOffsetSrc = (Y * SrcWidth * SrcChannelCount) + (X * SrcChannelCount);
size_t ImgOffsetDest = (Y * SrcWidth * DstChannelCount) + (X * DstChannelCount);
size_t CopySize = SrcChannelCount;
if(SrcChannelCount == 3)
{
mem_copy(&pDest[ImgOffsetDest], &pSrc[ImgOffsetSrc], CopySize);
pDest[ImgOffsetDest + 3] = 255;
}
else if(SrcChannelCount == 1)
{
pDest[ImgOffsetDest + 0] = 255;
pDest[ImgOffsetDest + 1] = 255;
pDest[ImgOffsetDest + 2] = 255;
pDest[ImgOffsetDest + 3] = pSrc[ImgOffsetSrc];
}
}
}
return false;
}
}
int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y, int Width, int Height, int Format, const void *pData)
{
CCommandBuffer::SCommand_Texture_Update Cmd;
@ -313,14 +340,14 @@ int CGraphics_Threaded::LoadTextureRawSub(CTextureHandle TextureID, int x, int y
Cmd.m_Y = y;
Cmd.m_Width = Width;
Cmd.m_Height = Height;
Cmd.m_Format = ImageFormatToTexFormat(Format);
Cmd.m_Format = CCommandBuffer::TEXFORMAT_RGBA;
// calculate memory usage
int MemSize = Width * Height * ImageFormatToPixelSize(Format);
int MemSize = Width * Height * 4;
// copy texture data
void *pTmpData = malloc(MemSize);
mem_copy(pTmpData, pData, MemSize);
ConvertToRGBA((uint8_t *)pTmpData, (const uint8_t *)pData, Width, Height, Format);
Cmd.m_pData = pTmpData;
AddCmd(
@ -365,7 +392,7 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadSpriteTexture(CImageInfo &From
bool CGraphics_Threaded::IsImageSubFullyTransparent(CImageInfo &FromImageInfo, int x, int y, int w, int h)
{
if(FromImageInfo.m_Format == CImageInfo::FORMAT_ALPHA || FromImageInfo.m_Format == CImageInfo::FORMAT_RGBA)
if(FromImageInfo.m_Format == CImageInfo::FORMAT_SINGLE_COMPONENT || FromImageInfo.m_Format == CImageInfo::FORMAT_RGBA)
{
uint8_t *pImgData = (uint8_t *)FromImageInfo.m_pData;
int bpp = ImageFormatToPixelSize(FromImageInfo.m_Format);
@ -445,9 +472,9 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(int Width, int Heig
Cmd.m_Slot = Tex;
Cmd.m_Width = Width;
Cmd.m_Height = Height;
Cmd.m_PixelSize = ImageFormatToPixelSize(Format);
Cmd.m_Format = ImageFormatToTexFormat(Format);
Cmd.m_StoreFormat = ImageFormatToTexFormat(StoreFormat);
Cmd.m_PixelSize = 4;
Cmd.m_Format = CCommandBuffer::TEXFORMAT_RGBA;
Cmd.m_StoreFormat = CCommandBuffer::TEXFORMAT_RGBA;
// flags
Cmd.m_Flags = 0;
@ -467,7 +494,10 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTextureRaw(int Width, int Heig
// copy texture data
int MemSize = Width * Height * Cmd.m_PixelSize;
void *pTmpData = malloc(MemSize);
mem_copy(pTmpData, pData, MemSize);
if(!ConvertToRGBA((uint8_t *)pTmpData, (const uint8_t *)pData, Width, Height, Format))
{
dbg_msg("graphics", "converted image %s to RGBA, consider making its file format RGBA", pTexName);
}
Cmd.m_pData = pTmpData;
AddCmd(
@ -500,6 +530,103 @@ IGraphics::CTextureHandle CGraphics_Threaded::LoadTexture(const char *pFilename,
return m_InvalidTexture;
}
bool CGraphics_Threaded::LoadTextTextures(int Width, int Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData)
{
if(Width == 0 || Height == 0)
return false;
// grab texture
int Tex = m_FirstFreeTexture;
if(Tex == -1)
{
size_t CurSize = m_TextureIndices.size();
m_TextureIndices.resize(CurSize * 2);
for(size_t i = 0; i < CurSize - 1; ++i)
{
m_TextureIndices[CurSize + i] = CurSize + i + 1;
}
m_TextureIndices.back() = -1;
Tex = CurSize;
}
m_FirstFreeTexture = m_TextureIndices[Tex];
m_TextureIndices[Tex] = -1;
int Tex2 = m_FirstFreeTexture;
if(Tex2 == -1)
{
size_t CurSize = m_TextureIndices.size();
m_TextureIndices.resize(CurSize * 2);
for(size_t i = 0; i < CurSize - 1; ++i)
{
m_TextureIndices[CurSize + i] = CurSize + i + 1;
}
m_TextureIndices.back() = -1;
Tex2 = CurSize;
}
m_FirstFreeTexture = m_TextureIndices[Tex2];
m_TextureIndices[Tex2] = -1;
CCommandBuffer::SCommand_TextTextures_Create Cmd;
Cmd.m_Slot = Tex;
Cmd.m_SlotOutline = Tex2;
Cmd.m_Width = Width;
Cmd.m_Height = Height;
Cmd.m_pTextData = pTextData;
Cmd.m_pTextOutlineData = pTextOutlineData;
AddCmd(
Cmd, [] { return true; }, "failed to load text textures.");
TextTexture = CreateTextureHandle(Tex);
TextOutlineTexture = CreateTextureHandle(Tex2);
return true;
}
bool CGraphics_Threaded::UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture)
{
CCommandBuffer::SCommand_TextTextures_Destroy Cmd;
Cmd.m_Slot = TextTexture.Id();
Cmd.m_SlotOutline = TextOutlineTexture.Id();
AddCmd(
Cmd, [] { return true; }, "failed to unload text textures.");
m_TextureIndices[TextTexture.Id()] = m_FirstFreeTexture;
m_FirstFreeTexture = TextTexture.Id();
m_TextureIndices[TextOutlineTexture.Id()] = m_FirstFreeTexture;
m_FirstFreeTexture = TextOutlineTexture.Id();
TextTexture.Invalidate();
TextOutlineTexture.Invalidate();
return true;
}
bool CGraphics_Threaded::UpdateTextTexture(CTextureHandle TextureID, int x, int y, int Width, int Height, const void *pData)
{
CCommandBuffer::SCommand_TextTexture_Update Cmd;
Cmd.m_Slot = TextureID.Id();
Cmd.m_X = x;
Cmd.m_Y = y;
Cmd.m_Width = Width;
Cmd.m_Height = Height;
// calculate memory usage
int MemSize = Width * Height;
// copy texture data
void *pTmpData = malloc(MemSize);
mem_copy(pTmpData, pData, MemSize);
Cmd.m_pData = pTmpData;
AddCmd(
Cmd, [] { return true; }, "failed to update text texture.");
return true;
}
int CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType)
{
char aCompleteFilename[IO_MAX_PATH_LENGTH];
@ -592,7 +719,7 @@ bool CGraphics_Threaded::CheckImageDivisibility(const char *pFileName, CImageInf
}
int ColorChannelCount = 4;
if(Img.m_Format == CImageInfo::FORMAT_ALPHA)
if(Img.m_Format == CImageInfo::FORMAT_SINGLE_COMPONENT)
ColorChannelCount = 1;
else if(Img.m_Format == CImageInfo::FORMAT_RGB)
ColorChannelCount = 3;
@ -612,7 +739,7 @@ bool CGraphics_Threaded::CheckImageDivisibility(const char *pFileName, CImageInf
bool CGraphics_Threaded::IsImageFormatRGBA(const char *pFileName, CImageInfo &Img)
{
if(Img.m_Format != CImageInfo::FORMAT_RGBA && Img.m_Format != CImageInfo::FORMAT_ALPHA)
if(Img.m_Format != CImageInfo::FORMAT_RGBA)
{
SWarning NewWarning;
char aText[128];
@ -660,14 +787,17 @@ void CGraphics_Threaded::KickCommandBuffer()
m_pCommandBuffer->Reset();
}
void CGraphics_Threaded::ScreenshotDirect()
bool CGraphics_Threaded::ScreenshotDirect()
{
// add swap command
CImageInfo Image;
mem_zero(&Image, sizeof(Image));
CCommandBuffer::SCommand_Screenshot Cmd;
bool DidSwap = false;
CCommandBuffer::SCommand_TrySwapAndScreenshot Cmd;
Cmd.m_pImage = &Image;
Cmd.m_pSwapped = &DidSwap;
AddCmd(
Cmd, [] { return true; }, "failed to take screenshot.");
@ -699,6 +829,8 @@ void CGraphics_Threaded::ScreenshotDirect()
free(Image.m_pData);
}
return DidSwap;
}
void CGraphics_Threaded::TextureSet(CTextureHandle TextureID)
@ -707,13 +839,14 @@ void CGraphics_Threaded::TextureSet(CTextureHandle TextureID)
m_State.m_Texture = TextureID.Id();
}
void CGraphics_Threaded::Clear(float r, float g, float b)
void CGraphics_Threaded::Clear(float r, float g, float b, bool ForceClearNow)
{
CCommandBuffer::SCommand_Clear Cmd;
Cmd.m_Color.r = r;
Cmd.m_Color.g = g;
Cmd.m_Color.b = b;
Cmd.m_Color.a = 0;
Cmd.m_ForceClear = ForceClearNow || m_IsForcedViewport;
AddCmd(
Cmd, [] { return true; }, "failed to clear graphics.");
}
@ -735,18 +868,6 @@ void CGraphics_Threaded::QuadsEnd()
m_Drawing = 0;
}
void CGraphics_Threaded::TextQuadsBegin()
{
QuadsBegin();
}
void CGraphics_Threaded::TextQuadsEnd(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor)
{
dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->TextQuadsEnd without begin");
FlushTextVertices(TextureSize, TextTextureIndex, TextOutlineTextureIndex, pOutlineTextColor);
m_Drawing = 0;
}
void CGraphics_Threaded::QuadsTex3DBegin()
{
QuadsBegin();
@ -876,7 +997,7 @@ void CGraphics_Threaded::ChangeColorOfCurrentQuadVertices(float r, float g, floa
void CGraphics_Threaded::ChangeColorOfQuadVertices(int QuadOffset, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
if(g_Config.m_GfxQuadAsTriangle && !m_IsNewOpenGL)
if(g_Config.m_GfxQuadAsTriangle && !m_GLUseTrianglesAsQuad)
{
m_aVertices[QuadOffset * 6].m_Color.r = r;
m_aVertices[QuadOffset * 6].m_Color.g = g;
@ -981,7 +1102,7 @@ void CGraphics_Threaded::QuadsTex3DDrawTL(const CQuadItem *pArray, int Num)
int CurNumVert = m_NumVertices;
int VertNum = 0;
if(g_Config.m_GfxQuadAsTriangle && !m_IsNewOpenGL)
if(g_Config.m_GfxQuadAsTriangle && !m_GLUseTrianglesAsQuad)
{
VertNum = 6;
}
@ -1008,7 +1129,7 @@ void CGraphics_Threaded::QuadsDrawFreeform(const CFreeformItem *pArray, int Num)
{
dbg_assert(m_Drawing == DRAWING_QUADS || m_Drawing == DRAWING_TRIANGLES, "called Graphics()->QuadsDrawFreeform without begin");
if((g_Config.m_GfxQuadAsTriangle && !m_IsNewOpenGL) || m_Drawing == DRAWING_TRIANGLES)
if((g_Config.m_GfxQuadAsTriangle && !m_GLUseTrianglesAsQuad) || m_Drawing == DRAWING_TRIANGLES)
{
for(int i = 0; i < Num; ++i)
{
@ -1103,25 +1224,25 @@ void CGraphics_Threaded::QuadsText(float x, float y, float Size, const char *pTe
}
}
void CGraphics_Threaded::RenderTileLayer(int BufferContainerIndex, float *pColor, char **pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffet)
void CGraphics_Threaded::RenderTileLayer(int BufferContainerIndex, float *pColor, char **pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffset)
{
if(NumIndicesOffet == 0)
if(NumIndicesOffset == 0)
return;
//add the VertexArrays and draw
// add the VertexArrays and draw
CCommandBuffer::SCommand_RenderTileLayer Cmd;
Cmd.m_State = m_State;
Cmd.m_IndicesDrawNum = NumIndicesOffet;
Cmd.m_IndicesDrawNum = NumIndicesOffset;
Cmd.m_BufferContainerIndex = BufferContainerIndex;
mem_copy(&Cmd.m_Color, pColor, sizeof(Cmd.m_Color));
void *Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffet);
void *Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffset);
if(Data == 0x0)
{
// kick command buffer and try again
KickCommandBuffer();
Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffet);
Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffset);
if(Data == 0x0)
{
dbg_msg("graphics", "failed to allocate data for vertices");
@ -1129,18 +1250,18 @@ void CGraphics_Threaded::RenderTileLayer(int BufferContainerIndex, float *pColor
}
}
Cmd.m_pIndicesOffsets = (char **)Data;
Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet));
Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffset));
if(!AddCmd(
Cmd, [&] {
Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffet);
Data = m_pCommandBuffer->AllocData((sizeof(char *) + sizeof(unsigned int)) * NumIndicesOffset);
if(Data == 0x0)
{
dbg_msg("graphics", "failed to allocate data for vertices");
return false;
}
Cmd.m_pIndicesOffsets = (char **)Data;
Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffet));
Cmd.m_pDrawCount = (unsigned int *)(((char *)Data) + (sizeof(char *) * NumIndicesOffset));
return true;
},
"failed to allocate memory for render command"))
@ -1148,10 +1269,11 @@ void CGraphics_Threaded::RenderTileLayer(int BufferContainerIndex, float *pColor
return;
}
mem_copy(Cmd.m_pIndicesOffsets, pOffsets, sizeof(char *) * NumIndicesOffet);
mem_copy(Cmd.m_pDrawCount, IndicedVertexDrawNum, sizeof(unsigned int) * NumIndicesOffet);
mem_copy(Cmd.m_pIndicesOffsets, pOffsets, sizeof(char *) * NumIndicesOffset);
mem_copy(Cmd.m_pDrawCount, IndicedVertexDrawNum, sizeof(unsigned int) * NumIndicesOffset);
//todo max indices group check!!
m_pCommandBuffer->AddRenderCalls(NumIndicesOffset);
// todo max indices group check!!
}
void CGraphics_Threaded::RenderBorderTiles(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, int JumpIndex, unsigned int DrawNum)
@ -1179,6 +1301,8 @@ void CGraphics_Threaded::RenderBorderTiles(int BufferContainerIndex, float *pCol
{
return;
}
m_pCommandBuffer->AddRenderCalls(1);
}
void CGraphics_Threaded::RenderBorderTileLines(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, unsigned int IndexDrawNum, unsigned int RedrawNum)
@ -1206,6 +1330,8 @@ void CGraphics_Threaded::RenderBorderTileLines(int BufferContainerIndex, float *
{
return;
}
m_pCommandBuffer->AddRenderCalls(1);
}
void CGraphics_Threaded::RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset)
@ -1213,7 +1339,7 @@ void CGraphics_Threaded::RenderQuadLayer(int BufferContainerIndex, SQuadRenderIn
if(QuadNum == 0)
return;
//add the VertexArrays and draw
// add the VertexArrays and draw
CCommandBuffer::SCommand_RenderQuadLayer Cmd;
Cmd.m_State = m_State;
Cmd.m_QuadNum = QuadNum;
@ -1241,6 +1367,8 @@ void CGraphics_Threaded::RenderQuadLayer(int BufferContainerIndex, SQuadRenderIn
}
mem_copy(Cmd.m_pQuadInfo, pQuadInfo, sizeof(SQuadRenderInfo) * QuadNum);
m_pCommandBuffer->AddRenderCalls(((QuadNum - 1) / gs_GraphicsMaxQuadsRenderCount) + 1);
}
void CGraphics_Threaded::RenderText(int BufferContainerIndex, int TextQuadNum, int TextureSize, int TextureTextIndex, int TextureTextOutlineIndex, float *pTextColor, float *pTextoutlineColor)
@ -1263,6 +1391,8 @@ void CGraphics_Threaded::RenderText(int BufferContainerIndex, int TextQuadNum, i
{
return;
}
m_pCommandBuffer->AddRenderCalls(1);
}
int CGraphics_Threaded::CreateQuadContainer(bool AutomaticUpload)
@ -1299,18 +1429,19 @@ void CGraphics_Threaded::QuadContainerUpload(int ContainerIndex)
if(Container.m_QuadBufferObjectIndex == -1)
{
size_t UploadDataSize = Container.m_Quads.size() * sizeof(SQuadContainer::SQuad);
Container.m_QuadBufferObjectIndex = CreateBufferObject(UploadDataSize, &Container.m_Quads[0]);
Container.m_QuadBufferObjectIndex = CreateBufferObject(UploadDataSize, &Container.m_Quads[0], 0);
}
else
{
size_t UploadDataSize = Container.m_Quads.size() * sizeof(SQuadContainer::SQuad);
RecreateBufferObject(Container.m_QuadBufferObjectIndex, UploadDataSize, &Container.m_Quads[0]);
RecreateBufferObject(Container.m_QuadBufferObjectIndex, UploadDataSize, &Container.m_Quads[0], 0);
}
if(Container.m_QuadBufferContainerIndex == -1)
{
SBufferContainerInfo Info;
Info.m_Stride = sizeof(CCommandBuffer::SVertex);
Info.m_VertBufferBindingIndex = Container.m_QuadBufferObjectIndex;
Info.m_Attributes.emplace_back();
SBufferContainerInfo::SAttribute *pAttr = &Info.m_Attributes.back();
@ -1319,7 +1450,6 @@ void CGraphics_Threaded::QuadContainerUpload(int ContainerIndex)
pAttr->m_Normalized = false;
pAttr->m_pOffset = 0;
pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
pAttr->m_VertBufferBindingIndex = Container.m_QuadBufferObjectIndex;
Info.m_Attributes.emplace_back();
pAttr = &Info.m_Attributes.back();
pAttr->m_DataTypeCount = 2;
@ -1327,7 +1457,6 @@ void CGraphics_Threaded::QuadContainerUpload(int ContainerIndex)
pAttr->m_Normalized = false;
pAttr->m_pOffset = (void *)(sizeof(float) * 2);
pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
pAttr->m_VertBufferBindingIndex = Container.m_QuadBufferObjectIndex;
Info.m_Attributes.emplace_back();
pAttr = &Info.m_Attributes.back();
pAttr->m_DataTypeCount = 4;
@ -1335,7 +1464,6 @@ void CGraphics_Threaded::QuadContainerUpload(int ContainerIndex)
pAttr->m_Normalized = true;
pAttr->m_pOffset = (void *)(sizeof(float) * 2 + sizeof(float) * 2);
pAttr->m_Type = GRAPHICS_TYPE_UNSIGNED_BYTE;
pAttr->m_VertBufferBindingIndex = Container.m_QuadBufferObjectIndex;
Container.m_QuadBufferContainerIndex = CreateBufferContainer(&Info);
}
@ -1481,6 +1609,8 @@ void CGraphics_Threaded::RenderQuadContainer(int ContainerIndex, int QuadOffset,
{
return;
}
m_pCommandBuffer->AddRenderCalls(1);
}
else
{
@ -1558,6 +1688,8 @@ void CGraphics_Threaded::RenderQuadContainerEx(int ContainerIndex, int QuadOffse
{
return;
}
m_pCommandBuffer->AddRenderCalls(1);
}
else
{
@ -1702,6 +1834,9 @@ void CGraphics_Threaded::RenderQuadContainerAsSpriteMultiple(int ContainerIndex,
}
mem_copy(Cmd.m_pRenderInfo, pRenderInfo, sizeof(IGraphics::SRenderSpriteInfo) * DrawCount);
m_pCommandBuffer->AddRenderCalls(((DrawCount - 1) / gs_GraphicsMaxParticlesRenderCount) + 1);
WrapNormal();
}
else
@ -1732,7 +1867,7 @@ void *CGraphics_Threaded::AllocCommandBufferData(unsigned AllocSize)
return pData;
}
int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadData, bool IsMovedPointer)
int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer)
{
int Index = -1;
if(m_FirstFreeBufferObjectIndex == -1)
@ -1751,6 +1886,7 @@ int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadD
Cmd.m_BufferIndex = Index;
Cmd.m_DataSize = UploadDataSize;
Cmd.m_DeletePointer = IsMovedPointer;
Cmd.m_Flags = CreateFlags;
if(IsMovedPointer)
{
@ -1803,7 +1939,7 @@ int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadD
{
size_t UpdateSize = (UploadDataSize > CMD_BUFFER_DATA_BUFFER_SIZE ? CMD_BUFFER_DATA_BUFFER_SIZE : UploadDataSize);
UpdateBufferObject(Index, UpdateSize, (((char *)pUploadData) + UploadDataOffset), (void *)UploadDataOffset);
UpdateBufferObjectInternal(Index, UpdateSize, (((char *)pUploadData) + UploadDataOffset), (void *)UploadDataOffset);
UploadDataOffset += UpdateSize;
UploadDataSize -= UpdateSize;
@ -1814,12 +1950,13 @@ int CGraphics_Threaded::CreateBufferObject(size_t UploadDataSize, void *pUploadD
return Index;
}
void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, bool IsMovedPointer)
void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer)
{
CCommandBuffer::SCommand_RecreateBufferObject Cmd;
Cmd.m_BufferIndex = BufferIndex;
Cmd.m_DataSize = UploadDataSize;
Cmd.m_DeletePointer = IsMovedPointer;
Cmd.m_Flags = CreateFlags;
if(IsMovedPointer)
{
@ -1872,7 +2009,7 @@ void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadData
{
size_t UpdateSize = (UploadDataSize > CMD_BUFFER_DATA_BUFFER_SIZE ? CMD_BUFFER_DATA_BUFFER_SIZE : UploadDataSize);
UpdateBufferObject(BufferIndex, UpdateSize, (((char *)pUploadData) + UploadDataOffset), (void *)UploadDataOffset);
UpdateBufferObjectInternal(BufferIndex, UpdateSize, (((char *)pUploadData) + UploadDataOffset), (void *)UploadDataOffset);
UploadDataOffset += UpdateSize;
UploadDataSize -= UpdateSize;
@ -1881,7 +2018,7 @@ void CGraphics_Threaded::RecreateBufferObject(int BufferIndex, size_t UploadData
}
}
void CGraphics_Threaded::UpdateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, void *pOffset, bool IsMovedPointer)
void CGraphics_Threaded::UpdateBufferObjectInternal(int BufferIndex, size_t UploadDataSize, void *pUploadData, void *pOffset, bool IsMovedPointer)
{
CCommandBuffer::SCommand_UpdateBufferObject Cmd;
Cmd.m_BufferIndex = BufferIndex;
@ -1924,7 +2061,7 @@ void CGraphics_Threaded::UpdateBufferObject(int BufferIndex, size_t UploadDataSi
}
}
void CGraphics_Threaded::CopyBufferObject(int WriteBufferIndex, int ReadBufferIndex, size_t WriteOffset, size_t ReadOffset, size_t CopyDataSize)
void CGraphics_Threaded::CopyBufferObjectInternal(int WriteBufferIndex, int ReadBufferIndex, size_t WriteOffset, size_t ReadOffset, size_t CopyDataSize)
{
CCommandBuffer::SCommand_CopyBufferObject Cmd;
Cmd.m_WriteBufferIndex = WriteBufferIndex;
@ -1978,6 +2115,7 @@ int CGraphics_Threaded::CreateBufferContainer(SBufferContainerInfo *pContainerIn
Cmd.m_BufferContainerIndex = Index;
Cmd.m_AttrCount = (int)pContainerInfo->m_Attributes.size();
Cmd.m_Stride = pContainerInfo->m_Stride;
Cmd.m_VertBufferBindingIndex = pContainerInfo->m_VertBufferBindingIndex;
Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)AllocCommandBufferData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
if(Cmd.m_Attributes == NULL)
@ -1998,10 +2136,9 @@ int CGraphics_Threaded::CreateBufferContainer(SBufferContainerInfo *pContainerIn
return -1;
}
mem_copy(Cmd.m_Attributes, &pContainerInfo->m_Attributes[0], Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
mem_copy(Cmd.m_Attributes, pContainerInfo->m_Attributes.data(), Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
for(auto &Attribute : pContainerInfo->m_Attributes)
m_VertexArrayInfo[Index].m_AssociatedBufferObjectIndices.push_back(Attribute.m_VertBufferBindingIndex);
m_VertexArrayInfo[Index].m_AssociatedBufferObjectIndex = pContainerInfo->m_VertBufferBindingIndex;
return Index;
}
@ -2023,36 +2160,28 @@ void CGraphics_Threaded::DeleteBufferContainer(int ContainerIndex, bool DestroyA
if(DestroyAllBO)
{
// delete all associated references
for(size_t i = 0; i < m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndices.size(); ++i)
int BufferObjectIndex = m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndex;
if(BufferObjectIndex != -1)
{
int BufferObjectIndex = m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndices[i];
if(BufferObjectIndex != -1)
{
// don't delete double entries
for(int &m_AssociatedBufferObjectIndice : m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndices)
{
if(BufferObjectIndex == m_AssociatedBufferObjectIndice)
m_AssociatedBufferObjectIndice = -1;
}
// clear the buffer object index
m_BufferObjectIndices[BufferObjectIndex] = m_FirstFreeBufferObjectIndex;
m_FirstFreeBufferObjectIndex = BufferObjectIndex;
}
// clear the buffer object index
m_BufferObjectIndices[BufferObjectIndex] = m_FirstFreeBufferObjectIndex;
m_FirstFreeBufferObjectIndex = BufferObjectIndex;
}
}
m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndices.clear();
m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndex = -1;
// also clear the buffer object index
m_VertexArrayInfo[ContainerIndex].m_FreeIndex = m_FirstFreeVertexArrayInfo;
m_FirstFreeVertexArrayInfo = ContainerIndex;
}
void CGraphics_Threaded::UpdateBufferContainer(int ContainerIndex, SBufferContainerInfo *pContainerInfo)
void CGraphics_Threaded::UpdateBufferContainerInternal(int ContainerIndex, SBufferContainerInfo *pContainerInfo)
{
CCommandBuffer::SCommand_UpdateBufferContainer Cmd;
Cmd.m_BufferContainerIndex = ContainerIndex;
Cmd.m_AttrCount = (int)pContainerInfo->m_Attributes.size();
Cmd.m_Stride = pContainerInfo->m_Stride;
Cmd.m_VertBufferBindingIndex = pContainerInfo->m_VertBufferBindingIndex;
Cmd.m_Attributes = (SBufferContainerInfo::SAttribute *)AllocCommandBufferData(Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
if(Cmd.m_Attributes == NULL)
@ -2073,11 +2202,9 @@ void CGraphics_Threaded::UpdateBufferContainer(int ContainerIndex, SBufferContai
return;
}
mem_copy(Cmd.m_Attributes, &pContainerInfo->m_Attributes[0], Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
mem_copy(Cmd.m_Attributes, pContainerInfo->m_Attributes.data(), Cmd.m_AttrCount * sizeof(SBufferContainerInfo::SAttribute));
m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndices.clear();
for(auto &Attribute : pContainerInfo->m_Attributes)
m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndices.push_back(Attribute.m_VertBufferBindingIndex);
m_VertexArrayInfo[ContainerIndex].m_AssociatedBufferObjectIndex = pContainerInfo->m_VertBufferBindingIndex;
}
void CGraphics_Threaded::IndicesNumRequiredNotify(unsigned int RequiredIndicesCount)
@ -2119,17 +2246,48 @@ int CGraphics_Threaded::IssueInit()
int r = m_pBackend->Init("DDNet Client", &g_Config.m_GfxScreen, &g_Config.m_GfxScreenWidth, &g_Config.m_GfxScreenHeight, &g_Config.m_GfxScreenRefreshRate, g_Config.m_GfxFsaaSamples, Flags, &g_Config.m_GfxDesktopWidth, &g_Config.m_GfxDesktopHeight, &m_ScreenWidth, &m_ScreenHeight, m_pStorage);
AddBackEndWarningIfExists();
m_IsNewOpenGL = m_pBackend->IsNewOpenGL();
m_OpenGLTileBufferingEnabled = m_IsNewOpenGL || m_pBackend->HasTileBuffering();
m_OpenGLQuadBufferingEnabled = m_IsNewOpenGL || m_pBackend->HasQuadBuffering();
m_OpenGLQuadContainerBufferingEnabled = m_IsNewOpenGL || m_pBackend->HasQuadContainerBuffering();
m_OpenGLTextBufferingEnabled = m_IsNewOpenGL || (m_OpenGLQuadContainerBufferingEnabled && m_pBackend->HasTextBuffering());
m_OpenGLHasTextureArrays = m_IsNewOpenGL || m_pBackend->Has2DTextureArrays();
m_GLUseTrianglesAsQuad = m_pBackend->UseTrianglesAsQuad();
m_GLTileBufferingEnabled = m_pBackend->HasTileBuffering();
m_GLQuadBufferingEnabled = m_pBackend->HasQuadBuffering();
m_GLQuadContainerBufferingEnabled = m_pBackend->HasQuadContainerBuffering();
m_GLTextBufferingEnabled = (m_GLQuadContainerBufferingEnabled && m_pBackend->HasTextBuffering());
m_GLHasTextureArrays = m_pBackend->Has2DTextureArrays();
m_ScreenHiDPIScale = m_ScreenWidth / (float)g_Config.m_GfxScreenWidth;
m_ScreenRefreshRate = g_Config.m_GfxScreenRefreshRate;
return r;
}
void CGraphics_Threaded::AdjustViewport(bool SendViewportChangeToBackend)
{
// adjust the viewport to only allow certain aspect ratios
// keep this in sync with backend_vulkan GetSwapImageSize's check
if(m_ScreenHeight > 4 * m_ScreenWidth / 5)
{
m_IsForcedViewport = true;
m_ScreenHeight = 4 * m_ScreenWidth / 5;
if(SendViewportChangeToBackend)
{
CCommandBuffer::SCommand_Update_Viewport Cmd;
Cmd.m_X = 0;
Cmd.m_Y = 0;
Cmd.m_Width = m_ScreenWidth;
Cmd.m_Height = m_ScreenHeight;
Cmd.m_ByResize = true;
if(!AddCmd(
Cmd, [] { return true; }, "failed to add resize command"))
{
return;
}
}
}
else
{
m_IsForcedViewport = false;
}
}
void CGraphics_Threaded::AddBackEndWarningIfExists()
{
const char *pErrStr = m_pBackend->GetErrorString();
@ -2162,76 +2320,76 @@ int CGraphics_Threaded::InitWindow()
return 0;
}
size_t OpenGLInitTryCount = 0;
while(ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_OPENGL_CONTEXT_FAILED || ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_OPENGL_VERSION_FAILED)
size_t GLInitTryCount = 0;
while(ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED || ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_VERSION_FAILED)
{
if(ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_OPENGL_CONTEXT_FAILED)
if(ErrorCode == EGraphicsBackendErrorCodes::GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED)
{
// try next smaller major/minor or patch version
if(g_Config.m_GfxOpenGLMajor >= 4)
if(g_Config.m_GfxGLMajor >= 4)
{
g_Config.m_GfxOpenGLMajor = 3;
g_Config.m_GfxOpenGLMinor = 3;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 3;
g_Config.m_GfxGLMinor = 3;
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 3 && g_Config.m_GfxOpenGLMinor >= 1)
else if(g_Config.m_GfxGLMajor == 3 && g_Config.m_GfxGLMinor >= 1)
{
g_Config.m_GfxOpenGLMajor = 3;
g_Config.m_GfxOpenGLMinor = 0;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 3;
g_Config.m_GfxGLMinor = 0;
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 3 && g_Config.m_GfxOpenGLMinor == 0)
else if(g_Config.m_GfxGLMajor == 3 && g_Config.m_GfxGLMinor == 0)
{
g_Config.m_GfxOpenGLMajor = 2;
g_Config.m_GfxOpenGLMinor = 1;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 2;
g_Config.m_GfxGLMinor = 1;
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 2 && g_Config.m_GfxOpenGLMinor >= 1)
else if(g_Config.m_GfxGLMajor == 2 && g_Config.m_GfxGLMinor >= 1)
{
g_Config.m_GfxOpenGLMajor = 2;
g_Config.m_GfxOpenGLMinor = 0;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 2;
g_Config.m_GfxGLMinor = 0;
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 2 && g_Config.m_GfxOpenGLMinor == 0)
else if(g_Config.m_GfxGLMajor == 2 && g_Config.m_GfxGLMinor == 0)
{
g_Config.m_GfxOpenGLMajor = 1;
g_Config.m_GfxOpenGLMinor = 5;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 1;
g_Config.m_GfxGLMinor = 5;
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 1 && g_Config.m_GfxOpenGLMinor == 5)
else if(g_Config.m_GfxGLMajor == 1 && g_Config.m_GfxGLMinor == 5)
{
g_Config.m_GfxOpenGLMajor = 1;
g_Config.m_GfxOpenGLMinor = 4;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 1;
g_Config.m_GfxGLMinor = 4;
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 1 && g_Config.m_GfxOpenGLMinor == 4)
else if(g_Config.m_GfxGLMajor == 1 && g_Config.m_GfxGLMinor == 4)
{
g_Config.m_GfxOpenGLMajor = 1;
g_Config.m_GfxOpenGLMinor = 3;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 1;
g_Config.m_GfxGLMinor = 3;
g_Config.m_GfxGLPatch = 0;
}
else if(g_Config.m_GfxOpenGLMajor == 1 && g_Config.m_GfxOpenGLMinor == 3)
else if(g_Config.m_GfxGLMajor == 1 && g_Config.m_GfxGLMinor == 3)
{
g_Config.m_GfxOpenGLMajor = 1;
g_Config.m_GfxOpenGLMinor = 2;
g_Config.m_GfxOpenGLPatch = 1;
g_Config.m_GfxGLMajor = 1;
g_Config.m_GfxGLMinor = 2;
g_Config.m_GfxGLPatch = 1;
}
else if(g_Config.m_GfxOpenGLMajor == 1 && g_Config.m_GfxOpenGLMinor == 2)
else if(g_Config.m_GfxGLMajor == 1 && g_Config.m_GfxGLMinor == 2)
{
g_Config.m_GfxOpenGLMajor = 1;
g_Config.m_GfxOpenGLMinor = 1;
g_Config.m_GfxOpenGLPatch = 0;
g_Config.m_GfxGLMajor = 1;
g_Config.m_GfxGLMinor = 1;
g_Config.m_GfxGLPatch = 0;
}
}
// new opengl version was set by backend, try again
// new gl version was set by backend, try again
ErrorCode = IssueInit();
if(ErrorCode == 0)
{
return 0;
}
if(++OpenGLInitTryCount >= 9)
if(++GLInitTryCount >= 9)
{
// try something else
break;
@ -2249,6 +2407,16 @@ int CGraphics_Threaded::InitWindow()
return 0;
}
// at the very end, just try to set to gl 1.4
{
g_Config.m_GfxGLMajor = 1;
g_Config.m_GfxGLMinor = 4;
g_Config.m_GfxGLPatch = 0;
if(IssueInit() == 0)
return 0;
}
dbg_msg("gfx", "out of ideas. failed to init graphics");
return -1;
@ -2308,6 +2476,8 @@ int CGraphics_Threaded::Init()
str_format(aBuf, sizeof(aBuf), "GPU version: %s", GetVersionString());
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "gfx", aBuf, GPUInfoPrintColor);
AdjustViewport(true);
return 0;
}
@ -2355,6 +2525,7 @@ bool CGraphics_Threaded::SetWindowScreen(int Index)
}
m_pBackend->GetViewportSize(m_ScreenWidth, m_ScreenHeight);
AdjustViewport(true);
m_ScreenHiDPIScale = m_ScreenWidth / (float)g_Config.m_GfxScreenWidth;
return true;
}
@ -2370,6 +2541,7 @@ void CGraphics_Threaded::Move(int x, int y)
const int CurScreen = m_pBackend->GetWindowScreen();
m_pBackend->UpdateDisplayMode(CurScreen);
m_pBackend->GetViewportSize(m_ScreenWidth, m_ScreenHeight);
AdjustViewport(true);
m_ScreenHiDPIScale = m_ScreenWidth / (float)g_Config.m_GfxScreenWidth;
}
@ -2406,9 +2578,7 @@ void CGraphics_Threaded::GotResized(int w, int h, int RefreshRate)
// if the size change event is triggered, set all parameters and change the viewport
m_pBackend->GetViewportSize(m_ScreenWidth, m_ScreenHeight);
// adjust the viewport to only allow certain aspect ratios
if(m_ScreenHeight > 4 * m_ScreenWidth / 5)
m_ScreenHeight = 4 * m_ScreenWidth / 5;
AdjustViewport(false);
m_ScreenRefreshRate = RefreshRate;
@ -2422,6 +2592,7 @@ void CGraphics_Threaded::GotResized(int w, int h, int RefreshRate)
Cmd.m_Y = 0;
Cmd.m_Width = m_ScreenWidth;
Cmd.m_Height = m_ScreenHeight;
Cmd.m_ByResize = true;
if(!AddCmd(
Cmd, [] { return true; }, "failed to add resize command"))
@ -2459,6 +2630,10 @@ void CGraphics_Threaded::WindowDestroyNtf(uint32_t WindowID)
{
return;
}
// wait
KickCommandBuffer();
WaitForIdle();
}
void CGraphics_Threaded::WindowCreateNtf(uint32_t WindowID)
@ -2473,6 +2648,10 @@ void CGraphics_Threaded::WindowCreateNtf(uint32_t WindowID)
{
return;
}
// wait
KickCommandBuffer();
WaitForIdle();
}
int CGraphics_Threaded::WindowActive()
@ -2521,14 +2700,16 @@ void CGraphics_Threaded::Swap()
}
}
// TODO: screenshot support
bool TookScreenshotAndSwapped = false;
if(m_DoScreenshot)
{
if(WindowActive())
ScreenshotDirect();
TookScreenshotAndSwapped = ScreenshotDirect();
m_DoScreenshot = false;
}
if(!TookScreenshotAndSwapped)
{
// add swap command
CCommandBuffer::SCommand_Swap Cmd;
@ -2630,6 +2811,11 @@ const char *CGraphics_Threaded::GetRendererString()
return m_pBackend->GetRendererString();
}
TGLBackendReadPresentedImageData &CGraphics_Threaded::GetReadPresentedImageDataFuncUnsafe()
{
return m_pBackend->GetReadPresentedImageDataFuncUnsafe();
}
int CGraphics_Threaded::GetVideoModes(CVideoMode *pModes, int MaxModes, int Screen)
{
if(g_Config.m_GfxDisplayAllVideoModes)

View file

@ -60,6 +60,9 @@ class CCommandBuffer
public:
CBuffer m_CmdBuffer;
size_t m_CommandCount = 0;
size_t m_RenderCallCount = 0;
CBuffer m_DataBuffer;
enum
@ -68,15 +71,16 @@ public:
MAX_VERTICES = 32 * 1024,
};
enum
enum ECommandBufferCMD
{
// commadn groups
CMDGROUP_CORE = 0, // commands that everyone has to implement
CMDGROUP_PLATFORM_OPENGL = 10000, // commands specific to a platform
CMDGROUP_PLATFORM_GL = 10000, // commands specific to a platform
CMDGROUP_PLATFORM_SDL = 20000,
//
CMD_NOP = CMDGROUP_CORE,
CMD_FIRST = CMDGROUP_CORE,
CMD_NOP = CMD_FIRST,
//
CMD_RUNBUFFER,
@ -88,13 +92,16 @@ public:
CMD_TEXTURE_CREATE,
CMD_TEXTURE_DESTROY,
CMD_TEXTURE_UPDATE,
CMD_TEXT_TEXTURES_CREATE,
CMD_TEXT_TEXTURES_DESTROY,
CMD_TEXT_TEXTURE_UPDATE,
// rendering
CMD_CLEAR,
CMD_RENDER,
CMD_RENDER_TEX3D,
//opengl 2.0+ commands (some are just emulated and only exist in opengl 3.3+)
// opengl 2.0+ commands (some are just emulated and only exist in opengl 3.3+)
CMD_CREATE_BUFFER_OBJECT, // create vbo
CMD_RECREATE_BUFFER_OBJECT, // recreate vbo
CMD_UPDATE_BUFFER_OBJECT, // update vbo
@ -112,7 +119,6 @@ public:
CMD_RENDER_BORDER_TILE_LINE, // render an amount of tiles multiple times
CMD_RENDER_QUAD_LAYER, // render a quad layer
CMD_RENDER_TEXT, // render text
CMD_RENDER_TEXT_STREAM, // render text stream
CMD_RENDER_QUAD_CONTAINER, // render a quad buffer container
CMD_RENDER_QUAD_CONTAINER_EX, // render a quad buffer container with extended parameters
CMD_RENDER_QUAD_CONTAINER_SPRITE_MULTIPLE, // render a quad buffer container as sprite multiple times
@ -123,20 +129,20 @@ public:
// misc
CMD_VSYNC,
CMD_SCREENSHOT,
CMD_TRY_SWAP_AND_SCREENSHOT,
CMD_UPDATE_VIEWPORT,
// in Android a window that minimizes gets destroyed
CMD_WINDOW_CREATE_NTF,
CMD_WINDOW_DESTROY_NTF,
CMD_COUNT,
};
enum
{
TEXFORMAT_INVALID = 0,
TEXFORMAT_RGB,
TEXFORMAT_RGBA,
TEXFORMAT_ALPHA,
TEXFLAG_NOMIPMAPS = 1,
TEXFLAG_TO_3D_TEXTURE = (1 << 3),
@ -208,6 +214,7 @@ public:
SCommand_Clear() :
SCommand(CMD_CLEAR) {}
SColorf m_Color;
bool m_ForceClear;
};
struct SCommand_Signal : public SCommand
@ -254,6 +261,8 @@ public:
bool m_DeletePointer;
void *m_pUploadData;
size_t m_DataSize;
int m_Flags; // @see EBufferObjectCreateFlags
};
struct SCommand_RecreateBufferObject : public SCommand
@ -266,6 +275,8 @@ public:
bool m_DeletePointer;
void *m_pUploadData;
size_t m_DataSize;
int m_Flags; // @see EBufferObjectCreateFlags
};
struct SCommand_UpdateBufferObject : public SCommand
@ -310,6 +321,7 @@ public:
int m_BufferContainerIndex;
int m_Stride;
int m_VertBufferBindingIndex;
int m_AttrCount;
SBufferContainerInfo::SAttribute *m_Attributes;
@ -323,6 +335,7 @@ public:
int m_BufferContainerIndex;
int m_Stride;
int m_VertBufferBindingIndex;
int m_AttrCount;
SBufferContainerInfo::SAttribute *m_Attributes;
@ -350,9 +363,9 @@ public:
SCommand_RenderTileLayer() :
SCommand(CMD_RENDER_TILE_LAYER) {}
SState m_State;
SColorf m_Color; //the color of the whole tilelayer -- already envelopped
SColorf m_Color; // the color of the whole tilelayer -- already envelopped
//the char offset of all indices that should be rendered, and the amount of renders
// the char offset of all indices that should be rendered, and the amount of renders
char **m_pIndicesOffsets;
unsigned int *m_pDrawCount;
@ -365,7 +378,7 @@ public:
SCommand_RenderBorderTile() :
SCommand(CMD_RENDER_BORDER_TILE) {}
SState m_State;
SColorf m_Color; //the color of the whole tilelayer -- already envelopped
SColorf m_Color; // the color of the whole tilelayer -- already envelopped
char *m_pIndicesOffset; // you should use the command buffer data to allocate vertices for this command
unsigned int m_DrawNum;
int m_BufferContainerIndex;
@ -380,7 +393,7 @@ public:
SCommand_RenderBorderTileLine() :
SCommand(CMD_RENDER_BORDER_TILE_LINE) {}
SState m_State;
SColorf m_Color; //the color of the whole tilelayer -- already envelopped
SColorf m_Color; // the color of the whole tilelayer -- already envelopped
char *m_pIndicesOffset; // you should use the command buffer data to allocate vertices for this command
unsigned int m_IndexDrawNum;
unsigned int m_DrawNum;
@ -419,24 +432,6 @@ public:
float m_aTextOutlineColor[4];
};
struct SCommand_RenderTextStream : public SCommand
{
SCommand_RenderTextStream() :
SCommand(CMD_RENDER_TEXT_STREAM) {}
SState m_State;
SVertex *m_pVertices;
unsigned m_PrimType;
unsigned m_PrimCount;
int m_TextureSize;
int m_TextTextureIndex;
int m_TextOutlineTextureIndex;
float m_aTextOutlineColor[4];
};
struct SCommand_RenderQuadContainer : public SCommand
{
SCommand_RenderQuadContainer() :
@ -484,11 +479,12 @@ public:
void *m_pOffset;
};
struct SCommand_Screenshot : public SCommand
struct SCommand_TrySwapAndScreenshot : public SCommand
{
SCommand_Screenshot() :
SCommand(CMD_SCREENSHOT) {}
SCommand_TrySwapAndScreenshot() :
SCommand(CMD_TRY_SWAP_AND_SCREENSHOT) {}
CImageInfo *m_pImage; // processor will fill this out, the one who adds this command must free the data as well
bool *m_pSwapped;
};
struct SCommand_Swap : public SCommand
@ -521,6 +517,7 @@ public:
int m_Y;
int m_Width;
int m_Height;
bool m_ByResize; // resized by an resize event.. a hint to make clear that the viewport update can be deferred if wanted
};
struct SCommand_Texture_Create : public SCommand
@ -565,6 +562,47 @@ public:
int m_Slot;
};
struct SCommand_TextTextures_Create : public SCommand
{
SCommand_TextTextures_Create() :
SCommand(CMD_TEXT_TEXTURES_CREATE) {}
// texture information
int m_Slot;
int m_SlotOutline;
int m_Width;
int m_Height;
void *m_pTextData;
void *m_pTextOutlineData;
};
struct SCommand_TextTextures_Destroy : public SCommand
{
SCommand_TextTextures_Destroy() :
SCommand(CMD_TEXT_TEXTURES_DESTROY) {}
// texture information
int m_Slot;
int m_SlotOutline;
};
struct SCommand_TextTexture_Update : public SCommand
{
SCommand_TextTexture_Update() :
SCommand(CMD_TEXT_TEXTURE_UPDATE) {}
// texture information
int m_Slot;
int m_X;
int m_Y;
int m_Width;
int m_Height;
void *m_pData; // will be freed by the command processor
};
struct SCommand_WindowCreateNtf : public CCommandBuffer::SCommand
{
SCommand_WindowCreateNtf() :
@ -611,6 +649,8 @@ public:
m_pCmdBufferHead = pCmd;
m_pCmdBufferTail = pCmd;
++m_CommandCount;
return true;
}
@ -624,6 +664,14 @@ public:
m_pCmdBufferHead = m_pCmdBufferTail = nullptr;
m_CmdBuffer.Reset();
m_DataBuffer.Reset();
m_CommandCount = 0;
m_RenderCallCount = 0;
}
void AddRenderCalls(size_t RenderCallCountToAdd)
{
m_RenderCallCount += RenderCallCountToAdd;
}
};
@ -631,8 +679,8 @@ enum EGraphicsBackendErrorCodes
{
GRAPHICS_BACKEND_ERROR_CODE_UNKNOWN = -1,
GRAPHICS_BACKEND_ERROR_CODE_NONE = 0,
GRAPHICS_BACKEND_ERROR_CODE_OPENGL_CONTEXT_FAILED,
GRAPHICS_BACKEND_ERROR_CODE_OPENGL_VERSION_FAILED,
GRAPHICS_BACKEND_ERROR_CODE_GL_CONTEXT_FAILED,
GRAPHICS_BACKEND_ERROR_CODE_GL_VERSION_FAILED,
GRAPHICS_BACKEND_ERROR_CODE_SDL_INIT_FAILED,
GRAPHICS_BACKEND_ERROR_CODE_SDL_SCREEN_REQUEST_FAILED,
GRAPHICS_BACKEND_ERROR_CODE_SDL_SCREEN_INFO_REQUEST_FAILED,
@ -660,7 +708,12 @@ public:
virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int *pRefreshRate, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, class IStorage *pStorage) = 0;
virtual int Shutdown() = 0;
virtual int MemoryUsage() const = 0;
virtual uint64_t TextureMemoryUsage() const = 0;
virtual uint64_t BufferMemoryUsage() const = 0;
virtual uint64_t StreamedMemoryUsage() const = 0;
virtual uint64_t StagingMemoryUsage() const = 0;
virtual const TTWGraphicsGPUList &GetGPUs() const = 0;
virtual void GetVideoModes(CVideoMode *pModes, int MaxModes, int *pNumModes, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int Screen) = 0;
virtual void GetCurrentVideoMode(CVideoMode &CurMode, int HiDPIScale, int MaxWindowWidth, int MaxWindowHeight, int Screen) = 0;
@ -685,13 +738,14 @@ public:
virtual void WindowCreateNtf(uint32_t WindowID) = 0;
virtual void RunBuffer(CCommandBuffer *pBuffer) = 0;
virtual void RunBufferSingleThreadedUnsafe(CCommandBuffer *pBuffer) = 0;
virtual bool IsIdle() const = 0;
virtual void WaitForIdle() = 0;
virtual void GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch) {}
virtual bool GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch, const char *&pName, EBackendType BackendType) = 0;
// checks if the current values of the config are a graphics modern API
virtual bool IsConfigModernAPI() { return false; }
virtual bool IsNewOpenGL() { return false; }
virtual bool UseTrianglesAsQuad() { return false; }
virtual bool HasTileBuffering() { return false; }
virtual bool HasQuadBuffering() { return false; }
virtual bool HasTextBuffering() { return false; }
@ -702,6 +756,9 @@ public:
virtual const char *GetVendorString() = 0;
virtual const char *GetVersionString() = 0;
virtual const char *GetRendererString() = 0;
// be aware that this function should only be called from the graphics thread, and even then you should really know what you are doing
virtual TGLBackendReadPresentedImageData &GetReadPresentedImageDataFuncUnsafe() = 0;
};
class CGraphics_Threaded : public IEngineGraphics
@ -717,12 +774,12 @@ class CGraphics_Threaded : public IEngineGraphics
CCommandBuffer::SState m_State;
IGraphicsBackend *m_pBackend;
bool m_OpenGLTileBufferingEnabled;
bool m_OpenGLQuadBufferingEnabled;
bool m_OpenGLTextBufferingEnabled;
bool m_OpenGLQuadContainerBufferingEnabled;
bool m_OpenGLHasTextureArrays;
bool m_IsNewOpenGL;
bool m_GLTileBufferingEnabled;
bool m_GLQuadBufferingEnabled;
bool m_GLTextBufferingEnabled;
bool m_GLQuadContainerBufferingEnabled;
bool m_GLHasTextureArrays;
bool m_GLUseTrianglesAsQuad;
CCommandBuffer *m_apCommandBuffers[NUM_CMDBUFFERS];
CCommandBuffer *m_pCommandBuffer;
@ -758,12 +815,16 @@ class CGraphics_Threaded : public IEngineGraphics
std::vector<SWarning> m_Warnings;
// is a non full windowed (in a sense that the viewport won't include the whole window),
// forced viewport, so that it justifies our UI ratio needs
bool m_IsForcedViewport = false;
struct SVertexArrayInfo
{
SVertexArrayInfo() :
m_FreeIndex(-1) {}
// keep a reference to them, so we can free their IDs
std::vector<int> m_AssociatedBufferObjectIndices;
// keep a reference to it, so we can free the ID
int m_AssociatedBufferObjectIndex;
int m_FreeIndex;
};
@ -858,6 +919,8 @@ class CGraphics_Threaded : public IEngineGraphics
void AddBackEndWarningIfExists();
void AdjustViewport(bool SendViewportChangeToBackend);
int IssueInit();
int InitWindow();
@ -874,7 +937,12 @@ public:
void WrapNormal() override;
void WrapClamp() override;
int MemoryUsage() const override;
uint64_t TextureMemoryUsage() const override;
uint64_t BufferMemoryUsage() const override;
uint64_t StreamedMemoryUsage() const override;
uint64_t StagingMemoryUsage() const override;
const TTWGraphicsGPUList &GetGPUs() const override;
void MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY) override;
void GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY) override;
@ -887,6 +955,10 @@ public:
IGraphics::CTextureHandle LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = NULL) override;
int LoadTextureRawSub(IGraphics::CTextureHandle TextureID, int x, int y, int Width, int Height, int Format, const void *pData) override;
bool LoadTextTextures(int Width, int Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData) override;
bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) override;
bool UpdateTextTexture(CTextureHandle TextureID, int x, int y, int Width, int Height, const void *pData) override;
CTextureHandle LoadSpriteTextureImpl(CImageInfo &FromImageInfo, int x, int y, int w, int h);
CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct CDataSprite *pSprite) override;
CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) override;
@ -905,16 +977,14 @@ public:
void CopyTextureBufferSub(uint8_t *pDestBuffer, uint8_t *pSourceBuffer, int FullWidth, int FullHeight, int ColorChannelCount, int SubOffsetX, int SubOffsetY, int SubCopyWidth, int SubCopyHeight) override;
void CopyTextureFromTextureBufferSub(uint8_t *pDestBuffer, int DestWidth, int DestHeight, uint8_t *pSourceBuffer, int SrcWidth, int SrcHeight, int ColorChannelCount, int SrcSubOffsetX, int SrcSubOffsetY, int SrcSubCopyWidth, int SrcSubCopyHeight) override;
void ScreenshotDirect();
bool ScreenshotDirect();
void TextureSet(CTextureHandle TextureID) override;
void Clear(float r, float g, float b) override;
void Clear(float r, float g, float b, bool ForceClearNow = false) override;
void QuadsBegin() override;
void QuadsEnd() override;
void TextQuadsBegin() override;
void TextQuadsEnd(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor) override;
void QuadsTex3DBegin() override;
void QuadsTex3DEnd() override;
void TrianglesBegin() override;
@ -956,7 +1026,7 @@ public:
dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsDrawTL without begin");
if(g_Config.m_GfxQuadAsTriangle && !m_IsNewOpenGL)
if(g_Config.m_GfxQuadAsTriangle && !m_GLUseTrianglesAsQuad)
{
for(int i = 0; i < Num; ++i)
{
@ -1084,7 +1154,7 @@ public:
if(m_Drawing == DRAWING_QUADS)
{
if(g_Config.m_GfxQuadAsTriangle && !m_IsNewOpenGL)
if(g_Config.m_GfxQuadAsTriangle && !m_GLUseTrianglesAsQuad)
{
PrimType = CCommandBuffer::PRIMTYPE_TRIANGLES;
PrimCount = NumVerts / 3;
@ -1142,29 +1212,30 @@ public:
{
return;
}
m_pCommandBuffer->AddRenderCalls(1);
}
void FlushVertices(bool KeepVertices = false) override;
void FlushTextVertices(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor) override;
void FlushVerticesTex3D() override;
void RenderTileLayer(int BufferContainerIndex, float *pColor, char **pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffet) override;
void RenderTileLayer(int BufferContainerIndex, float *pColor, char **pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffset) override;
void RenderBorderTiles(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, int JumpIndex, unsigned int DrawNum) override;
void RenderBorderTileLines(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, unsigned int IndexDrawNum, unsigned int RedrawNum) override;
void RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset) override;
void RenderText(int BufferContainerIndex, int TextQuadNum, int TextureSize, int TextureTextIndex, int TextureTextOutlineIndex, float *pTextColor, float *pTextoutlineColor) override;
// opengl 3.3 functions
int CreateBufferObject(size_t UploadDataSize, void *pUploadData, bool IsMovedPointer = false) override;
void RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, bool IsMovedPointer = false) override;
void UpdateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, void *pOffset, bool IsMovedPointer = false) override;
void CopyBufferObject(int WriteBufferIndex, int ReadBufferIndex, size_t WriteOffset, size_t ReadOffset, size_t CopyDataSize) override;
// modern GL functions
int CreateBufferObject(size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer = false) override;
void RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer = false) override;
void UpdateBufferObjectInternal(int BufferIndex, size_t UploadDataSize, void *pUploadData, void *pOffset, bool IsMovedPointer = false);
void CopyBufferObjectInternal(int WriteBufferIndex, int ReadBufferIndex, size_t WriteOffset, size_t ReadOffset, size_t CopyDataSize);
void DeleteBufferObject(int BufferIndex) override;
int CreateBufferContainer(SBufferContainerInfo *pContainerInfo) override;
// destroying all buffer objects means, that all referenced VBOs are destroyed automatically, so the user does not need to save references to them
void DeleteBufferContainer(int ContainerIndex, bool DestroyAllBO = true) override;
void UpdateBufferContainer(int ContainerIndex, SBufferContainerInfo *pContainerInfo) override;
void UpdateBufferContainerInternal(int ContainerIndex, SBufferContainerInfo *pContainerInfo);
void IndicesNumRequiredNotify(unsigned int RequiredIndicesCount) override;
int GetNumScreens() const override;
@ -1207,17 +1278,19 @@ public:
SWarning *GetCurWarning() override;
void GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch) override { m_pBackend->GetDriverVersion(DriverAgeType, Major, Minor, Patch); }
bool GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch, const char *&pName, EBackendType BackendType) override { return m_pBackend->GetDriverVersion(DriverAgeType, Major, Minor, Patch, pName, BackendType); }
bool IsConfigModernAPI() override { return m_pBackend->IsConfigModernAPI(); }
bool IsTileBufferingEnabled() override { return m_OpenGLTileBufferingEnabled; }
bool IsQuadBufferingEnabled() override { return m_OpenGLQuadBufferingEnabled; }
bool IsTextBufferingEnabled() override { return m_OpenGLTextBufferingEnabled; }
bool IsQuadContainerBufferingEnabled() override { return m_OpenGLQuadContainerBufferingEnabled; }
bool HasTextureArrays() override { return m_OpenGLHasTextureArrays; }
bool IsTileBufferingEnabled() override { return m_GLTileBufferingEnabled; }
bool IsQuadBufferingEnabled() override { return m_GLQuadBufferingEnabled; }
bool IsTextBufferingEnabled() override { return m_GLTextBufferingEnabled; }
bool IsQuadContainerBufferingEnabled() override { return m_GLQuadContainerBufferingEnabled; }
bool HasTextureArrays() override { return m_GLHasTextureArrays; }
const char *GetVendorString() override;
const char *GetVersionString() override;
const char *GetRendererString() override;
TGLBackendReadPresentedImageData &GetReadPresentedImageDataFuncUnsafe() override;
};
extern IGraphicsBackend *CreateGraphicsBackend();

View file

@ -28,7 +28,13 @@ public:
void WrapNormal() override{};
void WrapClamp() override{};
int MemoryUsage() const override { return 0; }
uint64_t TextureMemoryUsage() const override { return 0; }
uint64_t BufferMemoryUsage() const override { return 0; }
uint64_t StreamedMemoryUsage() const override { return 0; }
uint64_t StagingMemoryUsage() const override { return 0; }
TTWGraphicsGPUList m_FakeGPUList;
const TTWGraphicsGPUList &GetGPUs() const override { return m_FakeGPUList; }
void MapScreen(float TopLeftX, float TopLeftY, float BottomRightX, float BottomRightY) override{};
void GetScreen(float *pTopLeftX, float *pTopLeftY, float *pBottomRightX, float *pBottomRightY) override
@ -47,11 +53,20 @@ public:
IGraphics::CTextureHandle LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags, const char *pTexName = NULL) override { return CreateTextureHandle(0); }
int LoadTextureRawSub(IGraphics::CTextureHandle TextureID, int x, int y, int Width, int Height, int Format, const void *pData) override { return 0; }
CTextureHandle LoadSpriteTextureImpl(CImageInfo &FromImageInfo, int x, int y, int w, int h) { return CreateTextureHandle(0); }
CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct CDataSprite *pSprite) override { return CreateTextureHandle(0); }
CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) override { return CreateTextureHandle(0); }
bool LoadTextTextures(int Width, int Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData) override
{
return false;
}
bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) override { return false; }
bool UpdateTextTexture(CTextureHandle TextureID, int x, int y, int Width, int Height, const void *pData) override { return false; }
bool IsImageSubFullyTransparent(CImageInfo &FromImageInfo, int x, int y, int w, int h) override { return false; }
CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct CDataSprite *pSprite) override { return CreateTextureHandle(0); };
CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) override { return CreateTextureHandle(0); };
bool IsImageSubFullyTransparent(CImageInfo &FromImageInfo, int x, int y, int w, int h) override
{
return false;
}
bool IsSpriteTextureFullyTransparent(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) override { return false; }
// simple uncompressed RGBA loaders
@ -67,12 +82,10 @@ public:
void TextureSet(CTextureHandle TextureID) override{};
void Clear(float r, float g, float b) override{};
void Clear(float r, float g, float b, bool ForceClearNow = false) override{};
void QuadsBegin() override{};
void QuadsEnd() override{};
void TextQuadsBegin() override{};
void TextQuadsEnd(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor) override{};
void QuadsTex3DBegin() override{};
void QuadsTex3DEnd() override{};
void TrianglesBegin() override{};
@ -123,26 +136,22 @@ public:
void RenderQuadContainerAsSpriteMultiple(int ContainerIndex, int QuadOffset, int DrawCount, SRenderSpriteInfo *pRenderInfo) override{};
void FlushVertices(bool KeepVertices = false) override{};
void FlushTextVertices(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor) override{};
void FlushVerticesTex3D() override{};
void RenderTileLayer(int BufferContainerIndex, float *pColor, char **pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffet) override{};
void RenderTileLayer(int BufferContainerIndex, float *pColor, char **pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffset) override{};
void RenderBorderTiles(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, int JumpIndex, unsigned int DrawNum) override{};
void RenderBorderTileLines(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, unsigned int IndexDrawNum, unsigned int RedrawNum) override{};
void RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset) override{};
void RenderText(int BufferContainerIndex, int TextQuadNum, int TextureSize, int TextureTextIndex, int TextureTextOutlineIndex, float *pTextColor, float *pTextoutlineColor) override{};
// opengl 3.3 functions
int CreateBufferObject(size_t UploadDataSize, void *pUploadData, bool IsMovedPointer = false) override { return 0; }
void RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, bool IsMovedPointer = false) override{};
void UpdateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, void *pOffset, bool IsMovedPointer = false) override{};
void CopyBufferObject(int WriteBufferIndex, int ReadBufferIndex, size_t WriteOffset, size_t ReadOffset, size_t CopyDataSize) override{};
// modern GL functions
int CreateBufferObject(size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer = false) override { return 0; };
void RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer = false) override{};
void DeleteBufferObject(int BufferIndex) override{};
int CreateBufferContainer(SBufferContainerInfo *pContainerInfo) override { return 0; }
// destroying all buffer objects means, that all referenced VBOs are destroyed automatically, so the user does not need to save references to them
void DeleteBufferContainer(int ContainerIndex, bool DestroyAllBO = true) override{};
void UpdateBufferContainer(int ContainerIndex, SBufferContainerInfo *pContainerInfo) override{};
void IndicesNumRequiredNotify(unsigned int RequiredIndicesCount) override{};
int GetNumScreens() const override { return 0; }
@ -185,7 +194,7 @@ public:
SWarning *GetCurWarning() override { return NULL; }
void GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch) override {}
bool GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch, const char *&pName, EBackendType BackendType) override { return false; }
bool IsConfigModernAPI() override { return false; }
bool IsTileBufferingEnabled() override { return false; }
bool IsQuadBufferingEnabled() override { return false; }
@ -193,9 +202,15 @@ public:
bool IsQuadContainerBufferingEnabled() override { return false; }
bool HasTextureArrays() override { return false; }
const char *GetVendorString() override { return "headless"; }
const char *GetVersionString() override { return "headless"; }
const char *GetRendererString() override { return "headless"; }
const char *GetVendorString() override
{
return "headless";
};
const char *GetVersionString() override { return "headless"; };
const char *GetRendererString() override { return "headless"; };
TGLBackendReadPresentedImageData m_FakeGetPresentedImageDataFunc;
TGLBackendReadPresentedImageData &GetReadPresentedImageDataFuncUnsafe() override { return m_FakeGetPresentedImageDataFunc; };
};
#endif // ENGINE_CLIENT_GRAPHICS_THREADED_NULL_H

View file

@ -80,7 +80,7 @@ static std::atomic<int> m_SoundVolume{100};
static int m_NextVoice = 0;
static int *m_pMixBuffer = 0; // buffer only used by the thread callback function
static unsigned m_MaxFrames = 0;
static uint32_t m_MaxFrames = 0;
static const void *s_pWVBuffer = 0x0;
static int s_WVBufferPosition = 0;
@ -109,8 +109,8 @@ static int IntAbs(int i)
static void Mix(short *pFinalOut, unsigned Frames)
{
int MasterVol;
mem_zero(m_pMixBuffer, m_MaxFrames * 2 * sizeof(int));
Frames = minimum(Frames, m_MaxFrames);
mem_zero(m_pMixBuffer, Frames * 2 * sizeof(int));
// acquire lock while we are mixing
m_SoundLock.lock();
@ -284,9 +284,13 @@ static void SdlCallback(void *pUnused, Uint8 *pStream, int Len)
(void)pUnused;
#if defined(CONF_VIDEORECORDER)
if(!(IVideo::Current() && g_Config.m_ClVideoSndEnable))
Mix((short *)pStream, Len / 2 / 2);
{
Mix((short *)pStream, Len / sizeof(int16_t) / 2);
}
else
IVideo::Current()->NextAudioFrame(Mix);
{
mem_zero(pStream, Len);
}
#else
Mix((short *)pStream, Len / 2 / 2);
#endif
@ -331,6 +335,9 @@ int CSound::Init()
dbg_msg("client/sound", "sound init successful using audio driver '%s'", SDL_GetCurrentAudioDriver());
m_MaxFrames = FormatOut.samples * 2;
#if defined(CONF_VIDEORECORDER)
m_MaxFrames = maximum<uint32_t>(m_MaxFrames, 1024 * 2); // make the buffer bigger just in case
#endif
m_pMixBuffer = (int *)calloc(m_MaxFrames * 2, sizeof(int));
SDL_PauseAudioDevice(m_Device, 0);
@ -353,10 +360,6 @@ int CSound::Update()
std::unique_lock<std::mutex> Lock(m_SoundLock);
m_SoundVolume = WantedVolume;
}
//#if defined(CONF_VIDEORECORDER)
// if(IVideo::Current() && g_Config.m_ClVideoSndEnable)
// IVideo::Current()->NextAudioFrame(Mix);
//#endif
return 0;
}
@ -985,4 +988,19 @@ void CSound::StopVoice(CVoiceHandle Voice)
}
}
ISoundMixFunc CSound::GetSoundMixFunc()
{
return Mix;
}
void CSound::PauseAudioDevice()
{
SDL_PauseAudioDevice(m_Device, 1);
}
void CSound::UnpauseAudioDevice()
{
SDL_PauseAudioDevice(m_Device, 0);
}
IEngineSound *CreateEngineSound() { return new CSound; }

View file

@ -57,6 +57,10 @@ public:
virtual void Stop(int SampleID);
virtual void StopAll();
virtual void StopVoice(CVoiceHandle Voice);
virtual ISoundMixFunc GetSoundMixFunc();
virtual void PauseAudioDevice();
virtual void UnpauseAudioDevice();
};
#endif

View file

@ -131,7 +131,7 @@ public:
IGraphics::CTextureHandle m_aTextures[2];
// keep the full texture, because opengl doesn't provide texture copying
unsigned char *m_TextureData[2];
uint8_t *m_TextureData[2];
// width and height are the same
int m_CurTextureDimensions[2];
@ -179,6 +179,8 @@ struct STextContainer
bool m_HasCursor;
bool m_HasSelection;
bool m_SingleTimeUse;
void Reset()
{
m_pFont = NULL;
@ -199,6 +201,8 @@ struct STextContainer
m_HasCursor = false;
m_HasSelection = false;
m_SingleTimeUse = false;
}
};
@ -315,35 +319,23 @@ class CTextRender : public IEngineTextRender
}
}
IGraphics::CTextureHandle InitTexture(int Width, int Height, void *pUploadData = NULL)
void InitTextures(int Width, int Height, IGraphics::CTextureHandle (&aTextures)[2], uint8_t *(&aTextureData)[2])
{
void *pMem = NULL;
if(pUploadData)
{
pMem = pUploadData;
}
else
{
pMem = calloc((size_t)Width * Height, 1);
}
IGraphics::CTextureHandle Texture = Graphics()->LoadTextureRaw(Width, Height, CImageInfo::FORMAT_ALPHA, pMem, CImageInfo::FORMAT_ALPHA, IGraphics::TEXLOAD_NOMIPMAPS | IGraphics::TEXLOAD_NO_COMPRESSION);
if(!pUploadData)
free(pMem);
return Texture;
size_t NewTextureSize = (size_t)Width * (size_t)Height * 1;
void *pTmpTextData = malloc(NewTextureSize);
void *pTmpTextOutlineData = malloc(NewTextureSize);
mem_copy(pTmpTextData, aTextureData[0], NewTextureSize);
mem_copy(pTmpTextOutlineData, aTextureData[1], NewTextureSize);
Graphics()->LoadTextTextures(Width, Height, aTextures[0], aTextures[1], pTmpTextData, pTmpTextOutlineData);
}
void UnloadTexture(IGraphics::CTextureHandle Index)
void UnloadTextures(IGraphics::CTextureHandle (&aTextures)[2])
{
Graphics()->UnloadTexture(&Index);
Graphics()->UnloadTextTextures(aTextures[0], aTextures[1]);
}
void IncreaseFontTexture(CFont *pFont, int TextureIndex)
void IncreaseFontTextureImpl(CFont *pFont, int TextureIndex, int NewDimensions)
{
int NewDimensions = pFont->m_CurTextureDimensions[TextureIndex] * 2;
unsigned char *pTmpTexBuffer = new unsigned char[NewDimensions * NewDimensions];
mem_zero(pTmpTexBuffer, (size_t)NewDimensions * NewDimensions * sizeof(unsigned char));
@ -354,8 +346,6 @@ class CTextRender : public IEngineTextRender
pTmpTexBuffer[x + y * NewDimensions] = pFont->m_TextureData[TextureIndex][x + y * pFont->m_CurTextureDimensions[TextureIndex]];
}
}
UnloadTexture(pFont->m_aTextures[TextureIndex]);
pFont->m_aTextures[TextureIndex] = InitTexture(NewDimensions, NewDimensions, pTmpTexBuffer);
delete[] pFont->m_TextureData[TextureIndex];
pFont->m_TextureData[TextureIndex] = pTmpTexBuffer;
@ -363,6 +353,17 @@ class CTextRender : public IEngineTextRender
pFont->m_TextureSkyline[TextureIndex].m_CurHeightOfPixelColumn.resize(NewDimensions, 0);
}
void IncreaseFontTexture(CFont *pFont)
{
int NewDimensions = pFont->m_CurTextureDimensions[0] * 2;
UnloadTextures(pFont->m_aTextures);
IncreaseFontTextureImpl(pFont, 0, NewDimensions);
IncreaseFontTextureImpl(pFont, 1, NewDimensions);
InitTextures(NewDimensions, NewDimensions, pFont->m_aTextures, pFont->m_TextureData);
}
int AdjustOutlineThicknessToFontSize(int OutlineThickness, int FontSize)
{
if(FontSize > 48)
@ -381,7 +382,7 @@ class CTextRender : public IEngineTextRender
pFont->m_TextureData[TextureIndex][x + PosX + ((y + PosY) * pFont->m_CurTextureDimensions[TextureIndex])] = pData[x + y * Width];
}
}
Graphics()->LoadTextureRawSub(pFont->m_aTextures[TextureIndex], PosX, PosY, Width, Height, CImageInfo::FORMAT_ALPHA, pData);
Graphics()->UpdateTextTexture(pFont->m_aTextures[TextureIndex], PosX, PosY, Width, Height, pData);
}
// 128k * 2 of data used for rendering glyphs
@ -547,7 +548,7 @@ class CTextRender : public IEngineTextRender
// upload the glyph
while(!GetCharacterSpace(pFont, 0, (int)Width, (int)Height, X, Y))
{
IncreaseFontTexture(pFont, 0);
IncreaseFontTexture(pFont);
}
UploadGlyph(pFont, 0, X, Y, (int)Width, (int)Height, ms_aGlyphData);
@ -555,7 +556,7 @@ class CTextRender : public IEngineTextRender
while(!GetCharacterSpace(pFont, 1, (int)Width, (int)Height, X, Y))
{
IncreaseFontTexture(pFont, 1);
IncreaseFontTexture(pFont);
}
UploadGlyph(pFont, 1, X, Y, (int)Width, (int)Height, ms_aGlyphDataOutlined);
}
@ -665,6 +666,7 @@ public:
m_FirstFreeTextContainerIndex = -1;
m_DefaultTextContainerInfo.m_Stride = sizeof(STextCharQuadVertex);
m_DefaultTextContainerInfo.m_VertBufferBindingIndex = -1;
m_DefaultTextContainerInfo.m_Attributes.emplace_back();
SBufferContainerInfo::SAttribute *pAttr = &m_DefaultTextContainerInfo.m_Attributes.back();
@ -673,7 +675,6 @@ public:
pAttr->m_Normalized = false;
pAttr->m_pOffset = 0;
pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
pAttr->m_VertBufferBindingIndex = -1;
m_DefaultTextContainerInfo.m_Attributes.emplace_back();
pAttr = &m_DefaultTextContainerInfo.m_Attributes.back();
pAttr->m_DataTypeCount = 2;
@ -681,7 +682,6 @@ public:
pAttr->m_Normalized = false;
pAttr->m_pOffset = (void *)(sizeof(float) * 2);
pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
pAttr->m_VertBufferBindingIndex = -1;
m_DefaultTextContainerInfo.m_Attributes.emplace_back();
pAttr = &m_DefaultTextContainerInfo.m_Attributes.back();
pAttr->m_DataTypeCount = 4;
@ -689,7 +689,6 @@ public:
pAttr->m_Normalized = true;
pAttr->m_pOffset = (void *)(sizeof(float) * 2 + sizeof(float) * 2);
pAttr->m_Type = GRAPHICS_TYPE_UNSIGNED_BYTE;
pAttr->m_VertBufferBindingIndex = -1;
IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
char aFilename[IO_MAX_PATH_LENGTH];
@ -727,8 +726,7 @@ public:
pFont->m_TextureData[1] = new unsigned char[pFont->m_CurTextureDimensions[1] * pFont->m_CurTextureDimensions[1]];
mem_zero(pFont->m_TextureData[1], (size_t)pFont->m_CurTextureDimensions[1] * pFont->m_CurTextureDimensions[1] * sizeof(unsigned char));
pFont->m_aTextures[0] = InitTexture(pFont->m_CurTextureDimensions[0], pFont->m_CurTextureDimensions[0]);
pFont->m_aTextures[1] = InitTexture(pFont->m_CurTextureDimensions[1], pFont->m_CurTextureDimensions[1]);
InitTextures(pFont->m_CurTextureDimensions[0], pFont->m_CurTextureDimensions[0], pFont->m_aTextures, pFont->m_TextureData);
pFont->m_TextureSkyline[0].m_CurHeightOfPixelColumn.resize(pFont->m_CurTextureDimensions[0], 0);
pFont->m_TextureSkyline[1].m_CurHeightOfPixelColumn.resize(pFont->m_CurTextureDimensions[1], 0);
@ -906,7 +904,10 @@ public:
virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length)
{
int OldRenderFlags = m_RenderFlags;
m_RenderFlags |= TEXT_RENDER_FLAG_ONE_TIME_USE;
int TextCont = CreateTextContainer(pCursor, pText, Length);
m_RenderFlags = OldRenderFlags;
if(TextCont != -1)
{
if((pCursor->m_Flags & TEXTFLAG_RENDER) != 0)
@ -936,6 +937,8 @@ public:
STextContainer &TextContainer = GetTextContainer(ContainerIndex);
TextContainer.m_pFont = pFont;
TextContainer.m_SingleTimeUse = (m_RenderFlags & TEXT_RENDER_FLAG_ONE_TIME_USE) != 0;
CFontSizeData *pSizeData = NULL;
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
@ -1405,7 +1408,7 @@ public:
if(TextContainer.m_StringInfo.m_QuadBufferObjectIndex != -1 && (TextContainer.m_RenderFlags & TEXT_RENDER_FLAG_NO_AUTOMATIC_QUAD_UPLOAD) == 0)
{
Graphics()->RecreateBufferObject(TextContainer.m_StringInfo.m_QuadBufferObjectIndex, DataSize, pUploadData);
Graphics()->RecreateBufferObject(TextContainer.m_StringInfo.m_QuadBufferObjectIndex, DataSize, pUploadData, TextContainer.m_SingleTimeUse ? IGraphics::EBufferObjectCreateFlags::BUFFER_OBJECT_CREATE_FLAGS_ONE_TIME_USE_BIT : 0);
Graphics()->IndicesNumRequiredNotify(TextContainer.m_StringInfo.m_QuadNum * 6);
}
}
@ -1524,11 +1527,10 @@ public:
{
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
size_t DataSize = TextContainer.m_StringInfo.m_CharacterQuads.size() * sizeof(STextCharQuad);
void *pUploadData = &TextContainer.m_StringInfo.m_CharacterQuads[0];
TextContainer.m_StringInfo.m_QuadBufferObjectIndex = Graphics()->CreateBufferObject(DataSize, pUploadData);
void *pUploadData = TextContainer.m_StringInfo.m_CharacterQuads.data();
TextContainer.m_StringInfo.m_QuadBufferObjectIndex = Graphics()->CreateBufferObject(DataSize, pUploadData, TextContainer.m_SingleTimeUse ? IGraphics::EBufferObjectCreateFlags::BUFFER_OBJECT_CREATE_FLAGS_ONE_TIME_USE_BIT : 0);
for(auto &Attribute : m_DefaultTextContainerInfo.m_Attributes)
Attribute.m_VertBufferBindingIndex = TextContainer.m_StringInfo.m_QuadBufferObjectIndex;
m_DefaultTextContainerInfo.m_VertBufferBindingIndex = TextContainer.m_StringInfo.m_QuadBufferObjectIndex;
TextContainer.m_StringInfo.m_QuadBufferContainerIndex = Graphics()->CreateBufferContainer(&m_DefaultTextContainerInfo);
Graphics()->IndicesNumRequiredNotify(TextContainer.m_StringInfo.m_QuadNum * 6);
@ -1920,7 +1922,7 @@ public:
k = 0;
mem_zero(pFont->m_TextureData[j], (size_t)pFont->m_CurTextureDimensions[j] * pFont->m_CurTextureDimensions[j] * sizeof(unsigned char));
Graphics()->LoadTextureRawSub(pFont->m_aTextures[j], 0, 0, pFont->m_CurTextureDimensions[j], pFont->m_CurTextureDimensions[j], CImageInfo::FORMAT_ALPHA, pFont->m_TextureData[j]);
Graphics()->UpdateTextTexture(pFont->m_aTextures[j], 0, 0, pFont->m_CurTextureDimensions[j], pFont->m_CurTextureDimensions[j], pFont->m_TextureData[j]);
}
pFont->InitFontSizes();

View file

@ -4,34 +4,28 @@
#include <engine/shared/config.h>
#include <engine/storage.h>
#include "video.h"
#include <engine/client/graphics_threaded.h>
#include <engine/sound.h>
#ifndef CONF_BACKEND_OPENGL_ES
#include <GL/glew.h>
#else
#include <GLES3/gl3.h>
#endif
#include <memory>
#include <mutex>
#include "video.h"
// This code is mostly stolen from https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/muxing.c
#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P /* default pix_fmt */
const size_t FORMAT_NCHANNELS = 3;
const size_t FORMAT_GL_NCHANNELS = 4;
LOCK g_WriteLock = 0;
CVideo::CVideo(CGraphics_Threaded *pGraphics, IStorage *pStorage, IConsole *pConsole, int Width, int Height, const char *pName) :
CVideo::CVideo(CGraphics_Threaded *pGraphics, ISound *pSound, IStorage *pStorage, IConsole *pConsole, int Width, int Height, const char *pName) :
m_pGraphics(pGraphics),
m_pStorage(pStorage),
m_pConsole(pConsole),
m_VideoStream(),
m_AudioStream()
m_pSound(pSound)
{
m_pPixels = 0;
m_pFormatContext = 0;
m_pFormat = 0;
m_pRGB = 0;
m_pOptDict = 0;
m_VideoCodec = 0;
@ -45,17 +39,14 @@ CVideo::CVideo(CGraphics_Threaded *pGraphics, IStorage *pStorage, IConsole *pCon
m_Recording = false;
m_Started = false;
m_ProcessingVideoFrame = false;
m_ProcessingAudioFrame = false;
m_ProcessingVideoFrame = 0;
m_ProcessingAudioFrame = 0;
m_NextFrame = false;
m_NextAudioFrame = false;
// TODO:
m_HasAudio = g_Config.m_ClVideoSndEnable;
m_SndBufferSize = g_Config.m_SndBufferSize;
dbg_assert(ms_pCurrentVideo == 0, "ms_pCurrentVideo is NOT set to NULL while creating a new Video.");
ms_TickTime = time_freq() / m_FPS;
@ -71,6 +62,12 @@ CVideo::~CVideo()
void CVideo::Start()
{
// wait for the graphic thread to idle
m_pGraphics->WaitForIdle();
m_AudioStream = {};
m_VideoStream = {};
char aDate[20];
str_timestamp(aDate, sizeof(aDate));
char aBuf[256];
@ -101,10 +98,20 @@ void CVideo::Start()
m_pFormat = m_pFormatContext->oformat;
size_t NVals = FORMAT_NCHANNELS * m_Width * m_Height;
m_VideoThreads = std::thread::hardware_concurrency() + 2;
// audio gets a bit less
m_AudioThreads = (std::thread::hardware_concurrency() / 2) + 2;
m_CurVideoThreadIndex = 0;
m_CurAudioThreadIndex = 0;
size_t GLNVals = FORMAT_GL_NCHANNELS * m_Width * m_Height;
m_pPixels = (uint8_t *)malloc(GLNVals * sizeof(TWGLubyte));
m_pRGB = (uint8_t *)malloc(NVals * sizeof(uint8_t));
m_vPixelHelper.resize(m_VideoThreads);
for(size_t i = 0; i < m_VideoThreads; ++i)
{
m_vPixelHelper[i].resize(GLNVals * sizeof(uint8_t));
}
m_vBuffer.resize(m_AudioThreads);
/* Add the audio and video streams using the default format codecs
* and initialize the codecs. */
@ -128,6 +135,30 @@ void CVideo::Start()
dbg_msg("video_recorder", "No audio.");
}
m_vVideoThreads.resize(m_VideoThreads);
for(size_t i = 0; i < m_VideoThreads; ++i)
{
m_vVideoThreads[i] = std::make_unique<SVideoRecorderThread>();
}
for(size_t i = 0; i < m_VideoThreads; ++i)
{
std::unique_lock<std::mutex> Lock(m_vVideoThreads[i]->m_Mutex);
m_vVideoThreads[i]->m_Thread = std::thread([this, i]() { RunVideoThread(i == 0 ? (m_VideoThreads - 1) : (i - 1), i); });
m_vVideoThreads[i]->m_Cond.wait(Lock, [this, i]() -> bool { return m_vVideoThreads[i]->m_Started; });
}
m_vAudioThreads.resize(m_AudioThreads);
for(size_t i = 0; i < m_AudioThreads; ++i)
{
m_vAudioThreads[i] = std::make_unique<SAudioRecorderThread>();
}
for(size_t i = 0; i < m_AudioThreads; ++i)
{
std::unique_lock<std::mutex> Lock(m_vAudioThreads[i]->m_Mutex);
m_vAudioThreads[i]->m_Thread = std::thread([this, i]() { RunAudioThread(i == 0 ? (m_AudioThreads - 1) : (i - 1), i); });
m_vAudioThreads[i]->m_Cond.wait(Lock, [this, i]() -> bool { return m_vAudioThreads[i]->m_Started; });
}
/* Now that all the parameters are set, we can open the audio and
* video codecs and allocate the necessary encode buffers. */
if(!OpenVideo())
@ -153,13 +184,21 @@ void CVideo::Start()
}
}
if(!m_VideoStream.pSwsCtx)
m_VideoStream.m_vpSwsCtxs.reserve(m_VideoThreads);
for(size_t i = 0; i < m_VideoThreads; ++i)
{
m_VideoStream.pSwsCtx = sws_getCachedContext(
m_VideoStream.pSwsCtx,
m_VideoStream.pEnc->width, m_VideoStream.pEnc->height, AV_PIX_FMT_RGB24,
m_VideoStream.pEnc->width, m_VideoStream.pEnc->height, AV_PIX_FMT_YUV420P,
0, 0, 0, 0);
if(m_VideoStream.m_vpSwsCtxs.size() <= i)
m_VideoStream.m_vpSwsCtxs.emplace_back(nullptr);
if(!m_VideoStream.m_vpSwsCtxs[i])
{
m_VideoStream.m_vpSwsCtxs[i] = sws_getCachedContext(
m_VideoStream.m_vpSwsCtxs[i],
m_VideoStream.pEnc->width, m_VideoStream.pEnc->height, AV_PIX_FMT_RGBA,
m_VideoStream.pEnc->width, m_VideoStream.pEnc->height, AV_PIX_FMT_YUV420P,
0, 0, 0, 0);
}
}
/* Write the stream header, if any. */
@ -185,11 +224,37 @@ void CVideo::Pause(bool Pause)
void CVideo::Stop()
{
m_Recording = false;
m_pGraphics->WaitForIdle();
while(m_ProcessingVideoFrame || m_ProcessingAudioFrame)
for(size_t i = 0; i < m_VideoThreads; ++i)
{
{
std::unique_lock<std::mutex> Lock(m_vVideoThreads[i]->m_Mutex);
m_vVideoThreads[i]->m_Finished = true;
m_vVideoThreads[i]->m_Cond.notify_all();
}
m_vVideoThreads[i]->m_Thread.join();
}
m_vVideoThreads.clear();
for(size_t i = 0; i < m_AudioThreads; ++i)
{
{
std::unique_lock<std::mutex> Lock(m_vAudioThreads[i]->m_Mutex);
m_vAudioThreads[i]->m_Finished = true;
m_vAudioThreads[i]->m_Cond.notify_all();
}
m_vAudioThreads[i]->m_Thread.join();
}
m_vAudioThreads.clear();
while(m_ProcessingVideoFrame > 0 || m_ProcessingAudioFrame > 0)
thread_sleep(10);
m_Recording = false;
FinishFrames(&m_VideoStream);
if(m_HasAudio)
@ -209,11 +274,11 @@ void CVideo::Stop()
if(m_pFormatContext)
avformat_free_context(m_pFormatContext);
free(m_pRGB);
free(m_pPixels);
ISound *volatile pSound = m_pSound;
pSound->PauseAudioDevice();
delete ms_pCurrentVideo;
pSound->UnpauseAudioDevice();
}
void CVideo::NextVideoFrameThread()
@ -223,19 +288,52 @@ void CVideo::NextVideoFrameThread()
// #ifdef CONF_PLATFORM_MACOS
// CAutoreleasePool AutoreleasePool;
// #endif
m_Vseq += 1;
if(m_Vseq >= 2)
m_VSeq += 1;
if(m_VSeq >= 2)
{
m_ProcessingVideoFrame = true;
m_VideoStream.pFrame->pts = (int64_t)m_VideoStream.pEnc->frame_number;
m_ProcessingVideoFrame.fetch_add(1);
size_t NextVideoThreadIndex = m_CurVideoThreadIndex + 1;
if(NextVideoThreadIndex == m_VideoThreads)
NextVideoThreadIndex = 0;
// always wait for the next video thread too, to prevent a dead lock
{
auto *pVideoThread = m_vVideoThreads[NextVideoThreadIndex].get();
std::unique_lock<std::mutex> Lock(pVideoThread->m_Mutex);
if(pVideoThread->m_HasVideoFrame)
{
pVideoThread->m_Cond.wait(Lock, [&pVideoThread]() -> bool { return !pVideoThread->m_HasVideoFrame; });
}
}
//dbg_msg("video_recorder", "vframe: %d", m_VideoStream.pEnc->frame_number);
ReadRGBFromGL();
FillVideoFrame();
lock_wait(g_WriteLock);
WriteFrame(&m_VideoStream);
lock_unlock(g_WriteLock);
m_ProcessingVideoFrame = false;
// after reading the graphic libraries' frame buffer, go threaded
{
auto *pVideoThread = m_vVideoThreads[m_CurVideoThreadIndex].get();
std::unique_lock<std::mutex> Lock(pVideoThread->m_Mutex);
if(pVideoThread->m_HasVideoFrame)
{
pVideoThread->m_Cond.wait(Lock, [&pVideoThread]() -> bool { return !pVideoThread->m_HasVideoFrame; });
}
ReadRGBFromGL(m_CurVideoThreadIndex);
pVideoThread->m_HasVideoFrame = true;
{
std::unique_lock<std::mutex> LockParent(pVideoThread->m_VideoFillMutex);
pVideoThread->m_VideoFrameToFill = m_VSeq;
}
pVideoThread->m_Cond.notify_all();
}
++m_CurVideoThreadIndex;
if(m_CurVideoThreadIndex == m_VideoThreads)
m_CurVideoThreadIndex = 0;
}
m_NextFrame = false;
@ -266,129 +364,220 @@ void CVideo::NextVideoFrame()
}
}
void CVideo::NextAudioFrameTimeline()
void CVideo::NextAudioFrameTimeline(ISoundMixFunc Mix)
{
if(m_Recording && m_HasAudio)
{
//if(m_Vframe * m_AudioStream.pEnc->sample_rate / m_FPS >= m_AudioStream.pEnc->frame_number*m_AudioStream.pEnc->frame_size)
if(m_VideoStream.pEnc->frame_number * (double)m_AudioStream.pEnc->sample_rate / m_FPS >= (double)m_AudioStream.pEnc->frame_number * m_AudioStream.pEnc->frame_size)
//if(m_VideoStream.pEnc->frame_number * (double)m_AudioStream.pEnc->sample_rate / m_FPS >= (double)m_AudioStream.pEnc->frame_number * m_AudioStream.pEnc->frame_size)
double SamplesPerFrame = (double)m_AudioStream.pEnc->sample_rate / m_FPS;
while(m_AudioStream.m_SamplesFrameCount >= m_AudioStream.m_SamplesCount)
{
m_NextAudioFrame = true;
NextAudioFrame(Mix);
}
m_AudioStream.m_SamplesFrameCount += SamplesPerFrame;
}
}
void CVideo::NextAudioFrame(void (*Mix)(short *pFinalOut, unsigned Frames))
void CVideo::NextAudioFrame(ISoundMixFunc Mix)
{
if(m_NextAudioFrame && m_Recording && m_HasAudio)
if(m_Recording && m_HasAudio)
{
m_ProcessingAudioFrame = true;
//dbg_msg("video_recorder", "video_frame: %lf", (double)(m_Vframe/m_FPS));
//if((double)(m_Vframe/m_FPS) < m_AudioStream.pEnc->frame_number*m_AudioStream.pEnc->frame_size/m_AudioStream.pEnc->sample_rate)
//return;
Mix(m_aBuffer, ALEN);
//m_AudioStream.pFrame->pts = m_AudioStream.pEnc->frame_number;
//dbg_msg("video_recorder", "aframe: %d", m_AudioStream.pEnc->frame_number);
m_ASeq += 1;
// memcpy(m_AudioStream.pTmpFrame->data[0], pData, sizeof(int16_t) * m_SndBufferSize * 2);
//
// for(int i = 0; i < m_SndBufferSize; i++)
// {
// dbg_msg("video_recorder", "test: %d %d", ((int16_t*)pData)[i*2], ((int16_t*)pData)[i*2 + 1]);
// }
m_ProcessingAudioFrame.fetch_add(1);
int DstNbSamples;
size_t NextAudioThreadIndex = m_CurAudioThreadIndex + 1;
if(NextAudioThreadIndex == m_AudioThreads)
NextAudioThreadIndex = 0;
av_samples_fill_arrays(
(uint8_t **)m_AudioStream.pTmpFrame->data,
0, // pointer to linesize (int*)
(const uint8_t *)m_aBuffer,
2, // channels
m_AudioStream.pTmpFrame->nb_samples,
AV_SAMPLE_FMT_S16,
0 // align
);
// always wait for the next Audio thread too, to prevent a dead lock
DstNbSamples = av_rescale_rnd(
swr_get_delay(
m_AudioStream.pSwrCtx,
m_AudioStream.pEnc->sample_rate) +
m_AudioStream.pTmpFrame->nb_samples,
m_AudioStream.pEnc->sample_rate,
m_AudioStream.pEnc->sample_rate, AV_ROUND_UP);
// dbg_msg("video_recorder", "DstNbSamples: %d", DstNbSamples);
// fwrite(m_aBuffer, sizeof(short), 2048, m_dbgfile);
int Ret = av_frame_make_writable(m_AudioStream.pFrame);
if(Ret < 0)
{
dbg_msg("video_recorder", "Error making frame writable");
return;
auto *pAudioThread = m_vAudioThreads[NextAudioThreadIndex].get();
std::unique_lock<std::mutex> Lock(pAudioThread->m_Mutex);
if(pAudioThread->m_HasAudioFrame)
{
pAudioThread->m_Cond.wait(Lock, [&pAudioThread]() -> bool { return !pAudioThread->m_HasAudioFrame; });
}
}
/* convert to destination format */
Ret = swr_convert(
m_AudioStream.pSwrCtx,
m_AudioStream.pFrame->data,
m_AudioStream.pFrame->nb_samples,
(const uint8_t **)m_AudioStream.pTmpFrame->data,
m_AudioStream.pTmpFrame->nb_samples);
if(Ret < 0)
// after reading the graphic libraries' frame buffer, go threaded
{
dbg_msg("video_recorder", "Error while converting");
return;
auto *pAudioThread = m_vAudioThreads[m_CurAudioThreadIndex].get();
std::unique_lock<std::mutex> Lock(pAudioThread->m_Mutex);
if(pAudioThread->m_HasAudioFrame)
{
pAudioThread->m_Cond.wait(Lock, [&pAudioThread]() -> bool { return !pAudioThread->m_HasAudioFrame; });
}
Mix(m_vBuffer[m_CurAudioThreadIndex].m_aBuffer, ALEN / 2); // two channels
int64_t DstNbSamples = av_rescale_rnd(
swr_get_delay(m_AudioStream.m_vpSwrCtxs[m_CurAudioThreadIndex], m_AudioStream.pEnc->sample_rate) +
m_AudioStream.m_vpFrames[m_CurAudioThreadIndex]->nb_samples,
m_AudioStream.pEnc->sample_rate,
m_AudioStream.pEnc->sample_rate, AV_ROUND_UP);
pAudioThread->m_SampleCountStart = m_AudioStream.m_SamplesCount;
m_AudioStream.m_SamplesCount += DstNbSamples;
pAudioThread->m_HasAudioFrame = true;
{
std::unique_lock<std::mutex> LockParent(pAudioThread->m_AudioFillMutex);
pAudioThread->m_AudioFrameToFill = m_ASeq;
}
pAudioThread->m_Cond.notify_all();
}
// frame = ost->frame;
//
m_AudioStream.pFrame->pts = av_rescale_q(m_AudioStream.SamplesCount, AVRational{1, m_AudioStream.pEnc->sample_rate}, m_AudioStream.pEnc->time_base);
m_AudioStream.SamplesCount += DstNbSamples;
// dbg_msg("video_recorder", "prewrite----");
lock_wait(g_WriteLock);
WriteFrame(&m_AudioStream);
lock_unlock(g_WriteLock);
m_ProcessingAudioFrame = false;
m_NextAudioFrame = false;
++m_CurAudioThreadIndex;
if(m_CurAudioThreadIndex == m_AudioThreads)
m_CurAudioThreadIndex = 0;
}
}
void CVideo::FillAudioFrame()
void CVideo::RunAudioThread(size_t ParentThreadIndex, size_t ThreadIndex)
{
}
auto *pThreadData = m_vAudioThreads[ThreadIndex].get();
auto *pParentThreadData = m_vAudioThreads[ParentThreadIndex].get();
std::unique_lock<std::mutex> Lock(pThreadData->m_Mutex);
pThreadData->m_Started = true;
pThreadData->m_Cond.notify_all();
void CVideo::FillVideoFrame()
{
const int InLinesize[1] = {3 * m_VideoStream.pEnc->width};
sws_scale(m_VideoStream.pSwsCtx, (const uint8_t *const *)&m_pRGB, InLinesize, 0,
m_VideoStream.pEnc->height, m_VideoStream.pFrame->data, m_VideoStream.pFrame->linesize);
}
void CVideo::ReadRGBFromGL()
{
/* Get RGBA to align to 32 bits instead of just 24 for RGB. May be faster for FFmpeg. */
glReadBuffer(GL_FRONT);
GLint Alignment;
glGetIntegerv(GL_PACK_ALIGNMENT, &Alignment);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, m_Width, m_Height, GL_RGBA, GL_UNSIGNED_BYTE, m_pPixels);
glPixelStorei(GL_PACK_ALIGNMENT, Alignment);
for(int i = 0; i < m_Height; i++)
while(!pThreadData->m_Finished)
{
for(int j = 0; j < m_Width; j++)
pThreadData->m_Cond.wait(Lock, [&pThreadData]() -> bool { return pThreadData->m_HasAudioFrame || pThreadData->m_Finished; });
pThreadData->m_Cond.notify_all();
if(pThreadData->m_HasAudioFrame)
{
size_t CurGL = FORMAT_GL_NCHANNELS * (m_Width * (m_Height - i - 1) + j);
size_t CurRGB = FORMAT_NCHANNELS * (m_Width * i + j);
for(int k = 0; k < (int)FORMAT_NCHANNELS; k++)
m_pRGB[CurRGB + k] = m_pPixels[CurGL + k];
FillAudioFrame(ThreadIndex);
// check if we need to wait for the parent to finish
{
std::unique_lock<std::mutex> LockParent(pParentThreadData->m_AudioFillMutex);
if(pParentThreadData->m_AudioFrameToFill != 0 && pThreadData->m_AudioFrameToFill >= pParentThreadData->m_AudioFrameToFill)
{
// wait for the parent to finish its frame
pParentThreadData->m_AudioFillCond.wait(LockParent, [&pParentThreadData]() -> bool { return pParentThreadData->m_AudioFrameToFill == 0; });
}
}
{
std::unique_lock<std::mutex> LockAudio(pThreadData->m_AudioFillMutex);
lock_wait(g_WriteLock);
m_AudioStream.m_vpFrames[ThreadIndex]->pts = av_rescale_q(pThreadData->m_SampleCountStart, AVRational{1, m_AudioStream.pEnc->sample_rate}, m_AudioStream.pEnc->time_base);
WriteFrame(&m_AudioStream, ThreadIndex);
lock_unlock(g_WriteLock);
pThreadData->m_AudioFrameToFill = 0;
pThreadData->m_AudioFillCond.notify_all();
pThreadData->m_Cond.notify_all();
}
m_ProcessingAudioFrame.fetch_sub(1);
pThreadData->m_HasAudioFrame = false;
}
}
}
void CVideo::FillAudioFrame(size_t ThreadIndex)
{
av_samples_fill_arrays(
(uint8_t **)m_AudioStream.m_vpTmpFrames[ThreadIndex]->data,
0, // pointer to linesize (int*)
(const uint8_t *)m_vBuffer[ThreadIndex].m_aBuffer,
2, // channels
m_AudioStream.m_vpTmpFrames[ThreadIndex]->nb_samples,
AV_SAMPLE_FMT_S16,
0 // align
);
// dbg_msg("video_recorder", "DstNbSamples: %d", DstNbSamples);
// fwrite(m_aBuffer, sizeof(short), 2048, m_dbgfile);
int Ret = av_frame_make_writable(m_AudioStream.m_vpFrames[ThreadIndex]);
if(Ret < 0)
{
dbg_msg("video_recorder", "Error making frame writable");
return;
}
/* convert to destination format */
Ret = swr_convert(
m_AudioStream.m_vpSwrCtxs[ThreadIndex],
m_AudioStream.m_vpFrames[ThreadIndex]->data,
m_AudioStream.m_vpFrames[ThreadIndex]->nb_samples,
(const uint8_t **)m_AudioStream.m_vpTmpFrames[ThreadIndex]->data,
m_AudioStream.m_vpTmpFrames[ThreadIndex]->nb_samples);
if(Ret < 0)
{
dbg_msg("video_recorder", "Error while converting");
return;
}
}
void CVideo::RunVideoThread(size_t ParentThreadIndex, size_t ThreadIndex)
{
auto *pThreadData = m_vVideoThreads[ThreadIndex].get();
auto *pParentThreadData = m_vVideoThreads[ParentThreadIndex].get();
std::unique_lock<std::mutex> Lock(pThreadData->m_Mutex);
pThreadData->m_Started = true;
pThreadData->m_Cond.notify_all();
while(!pThreadData->m_Finished)
{
pThreadData->m_Cond.wait(Lock, [&pThreadData]() -> bool { return pThreadData->m_HasVideoFrame || pThreadData->m_Finished; });
pThreadData->m_Cond.notify_all();
if(pThreadData->m_HasVideoFrame)
{
FillVideoFrame(ThreadIndex);
// check if we need to wait for the parent to finish
{
std::unique_lock<std::mutex> LockParent(pParentThreadData->m_VideoFillMutex);
if(pParentThreadData->m_VideoFrameToFill != 0 && pThreadData->m_VideoFrameToFill >= pParentThreadData->m_VideoFrameToFill)
{
// wait for the parent to finish its frame
pParentThreadData->m_VideoFillCond.wait(LockParent, [&pParentThreadData]() -> bool { return pParentThreadData->m_VideoFrameToFill == 0; });
}
}
{
std::unique_lock<std::mutex> LockVideo(pThreadData->m_VideoFillMutex);
lock_wait(g_WriteLock);
m_VideoStream.m_vpFrames[ThreadIndex]->pts = (int64_t)m_VideoStream.pEnc->frame_number;
WriteFrame(&m_VideoStream, ThreadIndex);
lock_unlock(g_WriteLock);
pThreadData->m_VideoFrameToFill = 0;
pThreadData->m_VideoFillCond.notify_all();
pThreadData->m_Cond.notify_all();
}
m_ProcessingVideoFrame.fetch_sub(1);
pThreadData->m_HasVideoFrame = false;
}
}
}
void CVideo::FillVideoFrame(size_t ThreadIndex)
{
const int InLinesize[1] = {4 * m_VideoStream.pEnc->width};
auto *pRGBAData = m_vPixelHelper[ThreadIndex].data();
sws_scale(m_VideoStream.m_vpSwsCtxs[ThreadIndex], (const uint8_t *const *)&pRGBAData, InLinesize, 0,
m_VideoStream.pEnc->height, m_VideoStream.m_vpFrames[ThreadIndex]->data, m_VideoStream.m_vpFrames[ThreadIndex]->linesize);
}
void CVideo::ReadRGBFromGL(size_t ThreadIndex)
{
uint32_t Width;
uint32_t Height;
uint32_t Format;
m_pGraphics->GetReadPresentedImageDataFuncUnsafe()(Width, Height, Format, m_vPixelHelper[ThreadIndex]);
}
AVFrame *CVideo::AllocPicture(enum AVPixelFormat PixFmt, int Width, int Height)
{
AVFrame *pPicture;
@ -461,25 +650,39 @@ bool CVideo::OpenVideo()
return false;
}
m_VideoStream.m_vpFrames.clear();
m_VideoStream.m_vpFrames.reserve(m_VideoThreads);
/* allocate and init a re-usable frame */
m_VideoStream.pFrame = AllocPicture(c->pix_fmt, c->width, c->height);
if(!m_VideoStream.pFrame)
for(size_t i = 0; i < m_VideoThreads; ++i)
{
dbg_msg("video_recorder", "Could not allocate video frame");
return false;
m_VideoStream.m_vpFrames.emplace_back(nullptr);
m_VideoStream.m_vpFrames[i] = AllocPicture(c->pix_fmt, c->width, c->height);
if(!m_VideoStream.m_vpFrames[i])
{
dbg_msg("video_recorder", "Could not allocate video frame");
return false;
}
}
/* If the output format is not YUV420P, then a temporary YUV420P
* picture is needed too. It is then converted to the required
* output format. */
m_VideoStream.pTmpFrame = NULL;
m_VideoStream.m_vpTmpFrames.clear();
m_VideoStream.m_vpTmpFrames.reserve(m_VideoThreads);
if(c->pix_fmt != AV_PIX_FMT_YUV420P)
{
m_VideoStream.pTmpFrame = AllocPicture(AV_PIX_FMT_YUV420P, c->width, c->height);
if(!m_VideoStream.pTmpFrame)
/* allocate and init a re-usable frame */
for(size_t i = 0; i < m_VideoThreads; ++i)
{
dbg_msg("video_recorder", "Could not allocate temporary picture");
return false;
m_VideoStream.m_vpTmpFrames.emplace_back(nullptr);
m_VideoStream.m_vpTmpFrames[i] = AllocPicture(AV_PIX_FMT_YUV420P, c->width, c->height);
if(!m_VideoStream.m_vpTmpFrames[i])
{
dbg_msg("video_recorder", "Could not allocate temporary video frame");
return false;
}
}
}
@ -490,7 +693,7 @@ bool CVideo::OpenVideo()
dbg_msg("video_recorder", "Could not copy the stream parameters");
return false;
}
m_Vseq = 0;
m_VSeq = 0;
return true;
}
@ -521,9 +724,31 @@ bool CVideo::OpenAudio()
else
NbSamples = c->frame_size;
m_AudioStream.pFrame = AllocAudioFrame(c->sample_fmt, c->channel_layout, c->sample_rate, NbSamples);
m_AudioStream.m_vpFrames.clear();
m_AudioStream.m_vpFrames.reserve(m_AudioThreads);
m_AudioStream.pTmpFrame = AllocAudioFrame(AV_SAMPLE_FMT_S16, AV_CH_LAYOUT_STEREO, g_Config.m_SndRate, m_SndBufferSize * 2);
m_AudioStream.m_vpTmpFrames.clear();
m_AudioStream.m_vpTmpFrames.reserve(m_AudioThreads);
/* allocate and init a re-usable frame */
for(size_t i = 0; i < m_AudioThreads; ++i)
{
m_AudioStream.m_vpFrames.emplace_back(nullptr);
m_AudioStream.m_vpFrames[i] = AllocAudioFrame(c->sample_fmt, c->channel_layout, c->sample_rate, NbSamples);
if(!m_AudioStream.m_vpFrames[i])
{
dbg_msg("video_recorder", "Could not allocate audio frame");
return false;
}
m_AudioStream.m_vpTmpFrames.emplace_back(nullptr);
m_AudioStream.m_vpTmpFrames[i] = AllocAudioFrame(AV_SAMPLE_FMT_S16, AV_CH_LAYOUT_STEREO, g_Config.m_SndRate, NbSamples);
if(!m_AudioStream.m_vpTmpFrames[i])
{
dbg_msg("video_recorder", "Could not allocate audio frame");
return false;
}
}
/* copy the stream parameters to the muxer */
Ret = avcodec_parameters_from_context(m_AudioStream.pSt->codecpar, c);
@ -534,28 +759,34 @@ bool CVideo::OpenAudio()
}
/* create resampler context */
m_AudioStream.pSwrCtx = swr_alloc();
if(!m_AudioStream.pSwrCtx)
m_AudioStream.m_vpSwrCtxs.clear();
m_AudioStream.m_vpSwrCtxs.reserve(m_AudioThreads);
for(size_t i = 0; i < m_AudioThreads; ++i)
{
dbg_msg("video_recorder", "Could not allocate resampler context");
return false;
}
/* set options */
av_opt_set_int(m_AudioStream.pSwrCtx, "in_channel_count", 2, 0);
av_opt_set_int(m_AudioStream.pSwrCtx, "in_sample_rate", g_Config.m_SndRate, 0);
av_opt_set_sample_fmt(m_AudioStream.pSwrCtx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_int(m_AudioStream.pSwrCtx, "out_channel_count", c->channels, 0);
av_opt_set_int(m_AudioStream.pSwrCtx, "out_sample_rate", c->sample_rate, 0);
av_opt_set_sample_fmt(m_AudioStream.pSwrCtx, "out_sample_fmt", c->sample_fmt, 0);
/* initialize the resampling context */
if(swr_init(m_AudioStream.pSwrCtx) < 0)
{
dbg_msg("video_recorder", "Failed to initialize the resampling context");
return false;
m_AudioStream.m_vpSwrCtxs[i] = swr_alloc();
if(!m_AudioStream.m_vpSwrCtxs[i])
{
dbg_msg("video_recorder", "Could not allocate resampler context");
return false;
}
/* set options */
av_opt_set_int(m_AudioStream.m_vpSwrCtxs[i], "in_channel_count", 2, 0);
av_opt_set_int(m_AudioStream.m_vpSwrCtxs[i], "in_sample_rate", g_Config.m_SndRate, 0);
av_opt_set_sample_fmt(m_AudioStream.m_vpSwrCtxs[i], "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
av_opt_set_int(m_AudioStream.m_vpSwrCtxs[i], "out_channel_count", c->channels, 0);
av_opt_set_int(m_AudioStream.m_vpSwrCtxs[i], "out_sample_rate", c->sample_rate, 0);
av_opt_set_sample_fmt(m_AudioStream.m_vpSwrCtxs[i], "out_sample_fmt", c->sample_fmt, 0);
/* initialize the resampling context */
if(swr_init(m_AudioStream.m_vpSwrCtxs[i]) < 0)
{
dbg_msg("video_recorder", "Failed to initialize the resampling context");
return false;
}
}
m_ASeq = 0;
return true;
}
@ -591,18 +822,8 @@ bool CVideo::AddStream(OutputStream *pStream, AVFormatContext *pOC, const AVCode
switch((*ppCodec)->type)
{
case AVMEDIA_TYPE_AUDIO:
// m_MixingRate = g_Config.m_SndRate;
//
// // Set 16-bit stereo audio at 22Khz
// Format.freq = g_Config.m_SndRate;
// Format.format = AUDIO_S16;
// Format.channels = 2;
// Format.samples = g_Config.m_SndBufferSize;
c->sample_fmt = (*ppCodec)->sample_fmts ? (*ppCodec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
c->bit_rate = g_Config.m_SndRate * 2 * 16;
c->frame_size = m_SndBufferSize;
c->sample_rate = g_Config.m_SndRate;
if((*ppCodec)->supported_samplerates)
{
@ -610,7 +831,10 @@ bool CVideo::AddStream(OutputStream *pStream, AVFormatContext *pOC, const AVCode
for(int i = 0; (*ppCodec)->supported_samplerates[i]; i++)
{
if((*ppCodec)->supported_samplerates[i] == g_Config.m_SndRate)
{
c->sample_rate = g_Config.m_SndRate;
break;
}
}
}
c->channels = 2;
@ -670,7 +894,7 @@ bool CVideo::AddStream(OutputStream *pStream, AVFormatContext *pOC, const AVCode
return true;
}
void CVideo::WriteFrame(OutputStream *pStream)
void CVideo::WriteFrame(OutputStream *pStream, size_t ThreadIndex)
{
int RetRecv = 0;
@ -684,7 +908,7 @@ void CVideo::WriteFrame(OutputStream *pStream)
pPacket->data = 0;
pPacket->size = 0;
avcodec_send_frame(pStream->pEnc, pStream->pFrame);
avcodec_send_frame(pStream->pEnc, pStream->m_vpFrames[ThreadIndex]);
do
{
RetRecv = avcodec_receive_packet(pStream->pEnc, pPacket);
@ -761,10 +985,21 @@ void CVideo::FinishFrames(OutputStream *pStream)
void CVideo::CloseStream(OutputStream *pStream)
{
avcodec_free_context(&pStream->pEnc);
av_frame_free(&pStream->pFrame);
av_frame_free(&pStream->pTmpFrame);
sws_freeContext(pStream->pSwsCtx);
swr_free(&pStream->pSwrCtx);
for(auto *pFrame : pStream->m_vpFrames)
av_frame_free(&pFrame);
pStream->m_vpFrames.clear();
for(auto *pFrame : pStream->m_vpTmpFrames)
av_frame_free(&pFrame);
pStream->m_vpTmpFrames.clear();
for(auto *pSwsContext : pStream->m_vpSwsCtxs)
sws_freeContext(pSwsContext);
pStream->m_vpSwsCtxs.clear();
for(auto *pSwrContext : pStream->m_vpSwrCtxs)
swr_free(&pSwrContext);
pStream->m_vpSwrCtxs.clear();
}
#endif

View file

@ -16,6 +16,12 @@ extern "C" {
#include <engine/shared/demo.h>
#include <engine/shared/video.h>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
#define ALEN 2048
extern LOCK g_WriteLock;
@ -23,24 +29,25 @@ extern LOCK g_WriteLock;
// a wrapper around a single output AVStream
typedef struct OutputStream
{
AVStream *pSt;
AVCodecContext *pEnc;
AVStream *pSt = nullptr;
AVCodecContext *pEnc = nullptr;
/* pts of the next frame that will be generated */
int64_t NextPts;
int SamplesCount;
int64_t NextPts = 0;
int64_t m_SamplesCount = 0;
int64_t m_SamplesFrameCount = 0;
AVFrame *pFrame;
AVFrame *pTmpFrame;
std::vector<AVFrame *> m_vpFrames;
std::vector<AVFrame *> m_vpTmpFrames;
struct SwsContext *pSwsCtx;
struct SwrContext *pSwrCtx;
std::vector<struct SwsContext *> m_vpSwsCtxs;
std::vector<struct SwrContext *> m_vpSwrCtxs;
} OutputStream;
class CVideo : public IVideo
{
public:
CVideo(class CGraphics_Threaded *pGraphics, class IStorage *pStorage, class IConsole *pConsole, int width, int height, const char *name);
CVideo(class CGraphics_Threaded *pGraphics, class ISound *pSound, class IStorage *pStorage, class IConsole *pConsole, int width, int height, const char *name);
~CVideo();
virtual void Start();
@ -50,28 +57,28 @@ public:
virtual void NextVideoFrame();
virtual void NextVideoFrameThread();
virtual bool FrameRendered() { return !m_NextFrame; }
virtual void NextAudioFrame(void (*Mix)(short *pFinalOut, unsigned Frames));
virtual void NextAudioFrameTimeline();
virtual bool AudioFrameRendered() { return !m_NextAudioFrame; }
virtual void NextAudioFrame(ISoundMixFunc Mix);
virtual void NextAudioFrameTimeline(ISoundMixFunc Mix);
static IVideo *Current() { return IVideo::ms_pCurrentVideo; }
static void Init() { av_log_set_level(AV_LOG_DEBUG); }
private:
void FillVideoFrame();
void ReadRGBFromGL();
void RunVideoThread(size_t ParentThreadIndex, size_t ThreadIndex);
void FillVideoFrame(size_t ThreadIndex);
void ReadRGBFromGL(size_t ThreadIndex);
void FillAudioFrame();
void RunAudioThread(size_t ParentThreadIndex, size_t ThreadIndex);
void FillAudioFrame(size_t ThreadIndex);
bool OpenVideo();
bool OpenAudio();
AVFrame *AllocPicture(enum AVPixelFormat PixFmt, int Width, int Height);
AVFrame *AllocAudioFrame(enum AVSampleFormat SampleFmt, uint64_t ChannelLayout, int SampleRate, int NbSamples);
void WriteFrame(OutputStream *pStream) REQUIRES(g_WriteLock);
void WriteFrame(OutputStream *pStream, size_t ThreadIndex) REQUIRES(g_WriteLock);
void FinishFrames(OutputStream *pStream);
void CloseStream(OutputStream *pStream);
@ -79,30 +86,74 @@ private:
class CGraphics_Threaded *m_pGraphics;
class IStorage *m_pStorage;
class IConsole *m_pConsole;
class ISound *m_pSound;
int m_Width;
int m_Height;
char m_Name[256];
//FILE *m_dbgfile;
int m_Vseq;
short m_aBuffer[ALEN * 2];
int m_Vframe;
uint64_t m_VSeq = 0;
uint64_t m_ASeq = 0;
uint64_t m_Vframe;
int m_FPS;
bool m_Started;
bool m_Recording;
bool m_ProcessingVideoFrame;
bool m_ProcessingAudioFrame;
size_t m_VideoThreads = 2;
size_t m_CurVideoThreadIndex = 0;
size_t m_AudioThreads = 2;
size_t m_CurAudioThreadIndex = 0;
bool m_NextFrame;
bool m_NextAudioFrame;
struct SVideoRecorderThread
{
std::thread m_Thread;
std::mutex m_Mutex;
std::condition_variable m_Cond;
bool m_Started = false;
bool m_Finished = false;
bool m_HasVideoFrame = false;
std::mutex m_VideoFillMutex;
std::condition_variable m_VideoFillCond;
uint64_t m_VideoFrameToFill = 0;
};
std::vector<std::unique_ptr<SVideoRecorderThread>> m_vVideoThreads;
struct SAudioRecorderThread
{
std::thread m_Thread;
std::mutex m_Mutex;
std::condition_variable m_Cond;
bool m_Started = false;
bool m_Finished = false;
bool m_HasAudioFrame = false;
std::mutex m_AudioFillMutex;
std::condition_variable m_AudioFillCond;
uint64_t m_AudioFrameToFill = 0;
int64_t m_SampleCountStart = 0;
};
std::vector<std::unique_ptr<SAudioRecorderThread>> m_vAudioThreads;
std::atomic<int32_t> m_ProcessingVideoFrame;
std::atomic<int32_t> m_ProcessingAudioFrame;
std::atomic<bool> m_NextFrame;
bool m_HasAudio;
TWGLubyte *m_pPixels;
struct SVideoSoundBuffer
{
int16_t m_aBuffer[ALEN * 2];
};
std::vector<SVideoSoundBuffer> m_vBuffer;
std::vector<std::vector<uint8_t>> m_vPixelHelper;
OutputStream m_VideoStream;
OutputStream m_AudioStream;
@ -114,10 +165,6 @@ private:
AVFormatContext *m_pFormatContext;
const AVOutputFormat *m_pFormat;
uint8_t *m_pRGB;
int m_SndBufferSize;
};
#endif

View file

@ -21,6 +21,7 @@
struct SBufferContainerInfo
{
int m_Stride;
int m_VertBufferBindingIndex;
// the attributes of the container
struct SAttribute
@ -32,8 +33,6 @@ struct SBufferContainerInfo
//0: float, 1:integer
unsigned int m_FuncType;
int m_VertBufferBindingIndex;
};
std::vector<SAttribute> m_Attributes;
};
@ -43,6 +42,8 @@ struct SQuadRenderInfo
float m_aColor[4];
float m_aOffsets[2];
float m_Rotation;
// allows easier upload for uniform buffers because of the alignment requirements
float m_Padding;
};
struct SGraphicTile
@ -69,7 +70,7 @@ public:
FORMAT_AUTO = -1,
FORMAT_RGB = 0,
FORMAT_RGBA = 1,
FORMAT_ALPHA = 2,
FORMAT_SINGLE_COMPONENT = 2,
};
/* Variable: width
@ -152,19 +153,51 @@ struct GL_SVertexTex3DStream
GL_STexCoord3D m_Tex;
};
static constexpr size_t gs_GraphicsMaxQuadsRenderCount = 256;
static constexpr size_t gs_GraphicsMaxParticlesRenderCount = 512;
enum EGraphicsDriverAgeType
{
GRAPHICS_DRIVER_AGE_TYPE_LEGACY = 0,
GRAPHICS_DRIVER_AGE_TYPE_DEFAULT,
GRAPHICS_DRIVER_AGE_TYPE_MODERN,
GRAPHICS_DRIVER_AGE_TYPE_COUNT,
};
enum EBackendType
{
BACKEND_TYPE_OPENGL = 0,
BACKEND_TYPE_OPENGL_ES,
BACKEND_TYPE_VULKAN,
// special value to tell the backend to identify the current backend
BACKEND_TYPE_AUTO,
BACKEND_TYPE_COUNT,
};
struct STWGraphicGPU
{
struct STWGraphicGPUItem
{
char m_Name[256];
bool m_IsDiscreteGPU;
};
std::vector<STWGraphicGPUItem> m_GPUs;
STWGraphicGPUItem m_AutoGPU;
};
typedef STWGraphicGPU TTWGraphicsGPUList;
typedef std::function<void(void *)> WINDOW_RESIZE_FUNC;
namespace client_data7 {
struct CDataSprite; // NOLINT(bugprone-forward-declaration-namespace)
}
typedef std::function<bool(uint32_t &Width, uint32_t &Height, uint32_t &Format, std::vector<uint8_t> &DstData)> TGLBackendReadPresentedImageData;
class IGraphics : public IInterface
{
MACRO_INTERFACE("graphics", 0)
@ -221,7 +254,8 @@ public:
virtual void WindowDestroyNtf(uint32_t WindowID) = 0;
virtual void WindowCreateNtf(uint32_t WindowID) = 0;
virtual void Clear(float r, float g, float b) = 0;
// ForceClearNow forces the backend to trigger a clear, even at performance cost, else it might be delayed by one frame
virtual void Clear(float r, float g, float b, bool ForceClearNow = false) = 0;
virtual void ClipEnable(int x, int y, int w, int h) = 0;
virtual void ClipDisable() = 0;
@ -235,7 +269,13 @@ public:
virtual void BlendAdditive() = 0;
virtual void WrapNormal() = 0;
virtual void WrapClamp() = 0;
virtual int MemoryUsage() const = 0;
virtual uint64_t TextureMemoryUsage() const = 0;
virtual uint64_t BufferMemoryUsage() const = 0;
virtual uint64_t StreamedMemoryUsage() const = 0;
virtual uint64_t StagingMemoryUsage() const = 0;
virtual const TTWGraphicsGPUList &GetGPUs() const = 0;
virtual int LoadPNG(CImageInfo *pImg, const char *pFilename, int StorageType) = 0;
virtual void FreePNG(CImageInfo *pImg) = 0;
@ -256,6 +296,11 @@ public:
virtual void TextureSet(CTextureHandle Texture) = 0;
void TextureClear() { TextureSet(CTextureHandle()); }
// pTextData & pTextOutlineData are automatically free'd
virtual bool LoadTextTextures(int Width, int Height, CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture, void *pTextData, void *pTextOutlineData) = 0;
virtual bool UnloadTextTextures(CTextureHandle &TextTexture, CTextureHandle &TextOutlineTexture) = 0;
virtual bool UpdateTextTexture(CTextureHandle TextureID, int x, int y, int Width, int Height, const void *pData) = 0;
virtual CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct CDataSprite *pSprite) = 0;
virtual CTextureHandle LoadSpriteTexture(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) = 0;
@ -263,31 +308,35 @@ public:
virtual bool IsSpriteTextureFullyTransparent(CImageInfo &FromImageInfo, struct client_data7::CDataSprite *pSprite) = 0;
virtual void FlushVertices(bool KeepVertices = false) = 0;
virtual void FlushTextVertices(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor) = 0;
virtual void FlushVerticesTex3D() = 0;
// specific render functions
virtual void RenderTileLayer(int BufferContainerIndex, float *pColor, char **pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffet) = 0;
virtual void RenderTileLayer(int BufferContainerIndex, float *pColor, char **pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffset) = 0;
virtual void RenderBorderTiles(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, int JumpIndex, unsigned int DrawNum) = 0;
virtual void RenderBorderTileLines(int BufferContainerIndex, float *pColor, char *pIndexBufferOffset, float *pOffset, float *pDir, unsigned int IndexDrawNum, unsigned int RedrawNum) = 0;
virtual void RenderQuadLayer(int BufferContainerIndex, SQuadRenderInfo *pQuadInfo, int QuadNum, int QuadOffset) = 0;
virtual void RenderText(int BufferContainerIndex, int TextQuadNum, int TextureSize, int TextureTextIndex, int TextureTextOutlineIndex, float *pTextColor, float *pTextoutlineColor) = 0;
// opengl 3.3 functions
enum EBufferObjectCreateFlags
{
// tell the backend that the buffer only needs to be valid for the span of one frame. Buffer size is not allowed to be bigger than GL_SVertex * MAX_VERTICES
BUFFER_OBJECT_CREATE_FLAGS_ONE_TIME_USE_BIT = 1 << 0,
};
// if a pointer is passed as moved pointer, it requires to be allocated with malloc()
virtual int CreateBufferObject(size_t UploadDataSize, void *pUploadData, bool IsMovedPointer = false) = 0;
virtual void RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, bool IsMovedPointer = false) = 0;
virtual void UpdateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, void *pOffset, bool IsMovedPointer = false) = 0;
virtual void CopyBufferObject(int WriteBufferIndex, int ReadBufferIndex, size_t WriteOffset, size_t ReadOffset, size_t CopyDataSize) = 0;
virtual int CreateBufferObject(size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer = false) = 0;
virtual void RecreateBufferObject(int BufferIndex, size_t UploadDataSize, void *pUploadData, int CreateFlags, bool IsMovedPointer = false) = 0;
virtual void DeleteBufferObject(int BufferIndex) = 0;
virtual int CreateBufferContainer(struct SBufferContainerInfo *pContainerInfo) = 0;
// destroying all buffer objects means, that all referenced VBOs are destroyed automatically, so the user does not need to save references to them
virtual void DeleteBufferContainer(int ContainerIndex, bool DestroyAllBO = true) = 0;
virtual void UpdateBufferContainer(int ContainerIndex, struct SBufferContainerInfo *pContainerInfo) = 0;
virtual void IndicesNumRequiredNotify(unsigned int RequiredIndicesCount) = 0;
virtual void GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch) = 0;
// returns true if the driver age type is supported, passing BACKEND_TYPE_AUTO for BackendType will query the values for the currently used backend
virtual bool GetDriverVersion(EGraphicsDriverAgeType DriverAgeType, int &Major, int &Minor, int &Patch, const char *&pName, EBackendType BackendType) = 0;
virtual bool IsConfigModernAPI() = 0;
virtual bool IsTileBufferingEnabled() = 0;
virtual bool IsQuadBufferingEnabled() = 0;
@ -312,8 +361,6 @@ public:
virtual void QuadsBegin() = 0;
virtual void QuadsEnd() = 0;
virtual void TextQuadsBegin() = 0;
virtual void TextQuadsEnd(int TextureSize, int TextTextureIndex, int TextOutlineTextureIndex, float *pOutlineTextColor) = 0;
virtual void QuadsTex3DBegin() = 0;
virtual void QuadsTex3DEnd() = 0;
virtual void TrianglesBegin() = 0;
@ -413,6 +460,10 @@ public:
virtual void SetWindowGrab(bool Grab) = 0;
virtual void NotifyWindow() = 0;
// be aware that this function should only be called from the graphics thread, and even then you should really know what you are doing
// this function always returns the pixels in RGB
virtual TGLBackendReadPresentedImageData &GetReadPresentedImageDataFuncUnsafe() = 0;
virtual SWarning *GetCurWarning() = 0;
protected:

View file

@ -178,7 +178,7 @@ MACRO_CONFIG_INT(DbgCurl, dbg_curl, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SERVER, "D
MACRO_CONFIG_INT(DbgPref, dbg_pref, 0, 0, 1, CFGFLAG_SERVER, "Performance outputs")
MACRO_CONFIG_INT(DbgGraphs, dbg_graphs, 0, 0, 1, CFGFLAG_CLIENT, "Performance graphs")
MACRO_CONFIG_INT(DbgHitch, dbg_hitch, 0, 0, 0, CFGFLAG_SERVER, "Hitch warnings")
MACRO_CONFIG_INT(DbgGfx, dbg_gfx, 0, 0, 1, CFGFLAG_CLIENT, "Show OpenGL warnings and errors, if the GPU supports it")
MACRO_CONFIG_INT(DbgGfx, dbg_gfx, 0, 0, 4, CFGFLAG_CLIENT, "Show graphic library warnings and errors, if the GPU supports it (0: none, 1: minimal, 2: affects performance, 3: verbose, 4: all)")
#ifdef CONF_DEBUG
MACRO_CONFIG_INT(DbgStress, dbg_stress, 0, 0, 0, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Stress systems (Debug build only)")
MACRO_CONFIG_INT(DbgStressNetwork, dbg_stress_network, 0, 0, 0, CFGFLAG_CLIENT | CFGFLAG_SERVER, "Stress network (Debug build only)")
@ -307,8 +307,8 @@ MACRO_CONFIG_INT(ClShowOthersAlpha, cl_show_others_alpha, 40, 0, 100, CFGFLAG_CL
MACRO_CONFIG_INT(ClOverlayEntities, cl_overlay_entities, 0, 0, 100, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Overlay game tiles with a percentage of opacity")
MACRO_CONFIG_INT(ClShowQuads, cl_show_quads, 1, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Show quads")
MACRO_CONFIG_INT(ClZoomBackgroundLayers, cl_zoom_background_layers, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Zoom background layers")
MACRO_CONFIG_COL(ClBackgroundColor, cl_background_color, 128, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background color") //0 0 128
MACRO_CONFIG_COL(ClBackgroundEntitiesColor, cl_background_entities_color, 128, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background (entities) color") //0 0 128
MACRO_CONFIG_COL(ClBackgroundColor, cl_background_color, 128, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background color") // 0 0 128
MACRO_CONFIG_COL(ClBackgroundEntitiesColor, cl_background_entities_color, 128, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background (entities) color") // 0 0 128
MACRO_CONFIG_STR(ClBackgroundEntities, cl_background_entities, 100, "", CFGFLAG_CLIENT | CFGFLAG_SAVE, "Background (entities)")
MACRO_CONFIG_STR(ClRunOnJoin, cl_run_on_join, 100, "", CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Command to run when joining a server")
@ -401,22 +401,26 @@ MACRO_CONFIG_INT(ClDemoSliceEnd, cl_demo_slice_end, -1, 0, 0, CFGFLAG_SAVE | CFG
MACRO_CONFIG_INT(ClDemoShowSpeed, cl_demo_show_speed, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show speed meter on change")
MACRO_CONFIG_INT(ClDemoKeyboardShortcuts, cl_demo_keyboard_shortcuts, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Enable keyboard shortcuts in demo player")
// opengl
// graphic library
#ifndef CONF_ARCH_IA32
MACRO_CONFIG_INT(GfxOpenGLMajor, gfx_opengl_major, 3, 1, 10, CFGFLAG_SAVE | CFGFLAG_CLIENT, "OpenGL major version")
MACRO_CONFIG_INT(GfxGLMajor, gfx_gl_major, 3, 1, 10, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Graphic library major version")
#else
MACRO_CONFIG_INT(GfxOpenGLMajor, gfx_opengl_major, 1, 1, 10, CFGFLAG_SAVE | CFGFLAG_CLIENT, "OpenGL major version")
MACRO_CONFIG_INT(GfxGLMajor, gfx_gl_major, 1, 1, 10, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Graphic library major version")
#endif
MACRO_CONFIG_INT(GfxOpenGLMinor, gfx_opengl_minor, 0, 0, 10, CFGFLAG_SAVE | CFGFLAG_CLIENT, "OpenGL minor version")
MACRO_CONFIG_INT(GfxOpenGLPatch, gfx_opengl_patch, 0, 0, 10, CFGFLAG_SAVE | CFGFLAG_CLIENT, "OpenGL patch version")
MACRO_CONFIG_INT(GfxGLMinor, gfx_gl_minor, 0, 0, 10, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Graphic library minor version")
MACRO_CONFIG_INT(GfxGLPatch, gfx_gl_patch, 0, 0, 10, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Graphic library patch version")
// float multiplied with 1000
MACRO_CONFIG_INT(GfxOpenGLTextureLODBIAS, gfx_opengl_texture_lod_bias, -500, -15000, 15000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "The lod bias for OpenGL texture sampling multiplied by 1000")
MACRO_CONFIG_INT(GfxGLTextureLODBIAS, gfx_gl_texture_lod_bias, -500, -15000, 15000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "The lod bias for graphic library texture sampling multiplied by 1000")
MACRO_CONFIG_INT(Gfx3DTextureAnalysisDone, gfx_3d_texture_analysis_done, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Analyzed, if sampling 3D/2D array textures was correct")
MACRO_CONFIG_STR(Gfx3DTextureAnalysisRenderer, gfx_3d_texture_analysis_renderer, 128, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The renderer on which the analysis was performed")
MACRO_CONFIG_STR(Gfx3DTextureAnalysisVersion, gfx_3d_texture_analysis_version, 128, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The version on which the analysis was performed")
MACRO_CONFIG_STR(GfxGPUName, gfx_gpu_name, 256, "auto", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The GPU's name, which will be selected by the backend. (if supported by the backend)")
MACRO_CONFIG_STR(GfxBackend, gfx_backend, 256, "OpenGL", CFGFLAG_SAVE | CFGFLAG_CLIENT, "The backend to use (e.g. OpenGL or Vulkan)")
MACRO_CONFIG_INT(GfxRenderThreadCount, gfx_render_thread_count, 3, 0, 0, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Number of threads the backend can use for rendering. (note: the value can be ignored by the backend)")
MACRO_CONFIG_INT(GfxDriverIsBlocked, gfx_driver_is_blocked, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "If 1, the current driver is in a blocked error state.")
#if !defined(CONF_PLATFORM_MACOS)
MACRO_CONFIG_INT(GfxEnableTextureUnitOptimization, gfx_enable_texture_unit_optimization, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Use multiple texture units, instead of only one.")

View file

@ -3,6 +3,8 @@
#include <base/system.h>
typedef void (*ISoundMixFunc)(short *pFinalOut, unsigned Frames);
class IVideo
{
public:
@ -14,12 +16,10 @@ public:
virtual bool IsRecording() = 0;
virtual void NextVideoFrame() = 0;
virtual bool FrameRendered() = 0;
virtual void NextVideoFrameThread() = 0;
virtual void NextAudioFrame(void (*Mix)(short *pFinalOut, unsigned Frames)) = 0;
virtual bool AudioFrameRendered() = 0;
virtual void NextAudioFrameTimeline() = 0;
virtual void NextAudioFrameTimeline(ISoundMixFunc Mix) = 0;
static IVideo *Current() { return ms_pCurrentVideo; }

View file

@ -5,6 +5,8 @@
#include "kernel.h"
#include <engine/shared/video.h>
class ISound : public IInterface
{
MACRO_INTERFACE("sound", 0)
@ -86,6 +88,11 @@ public:
virtual void StopAll() = 0;
virtual void StopVoice(CVoiceHandle Voice) = 0;
virtual ISoundMixFunc GetSoundMixFunc() = 0;
// useful for thread synchronization
virtual void PauseAudioDevice() = 0;
virtual void UnpauseAudioDevice() = 0;
protected:
inline CVoiceHandle CreateVoiceHandle(int Index, int Age)
{

View file

@ -33,6 +33,8 @@ enum ETextRenderFlags
TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING = 1 << 6,
TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE = 1 << 7,
TEXT_RENDER_FLAG_NO_AUTOMATIC_QUAD_UPLOAD = 1 << 8,
// text is only rendered once and then discarded (a hint for buffer creation)
TEXT_RENDER_FLAG_ONE_TIME_USE = 1 << 9,
};
enum

View file

@ -553,9 +553,13 @@ void CHud::RenderTextInfo()
CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, m_Width - 10 - s_TextWidth[DigitIndex], 5, 12, TEXTFLAG_RENDER);
Cursor.m_LineWidth = -1;
auto OldFlags = TextRender()->GetRenderFlags();
TextRender()->SetRenderFlags(OldFlags | TEXT_RENDER_FLAG_ONE_TIME_USE);
if(m_FPSTextContainerIndex == -1)
m_FPSTextContainerIndex = TextRender()->CreateTextContainer(&Cursor, "0");
TextRender()->RecreateTextContainerSoft(&Cursor, m_FPSTextContainerIndex, aBuf);
else
TextRender()->RecreateTextContainerSoft(&Cursor, m_FPSTextContainerIndex, aBuf);
TextRender()->SetRenderFlags(OldFlags);
STextRenderColor TColor;
TColor.m_R = 1.f;
TColor.m_G = 1.f;

View file

@ -189,7 +189,9 @@ IGraphics::CTextureHandle CMapImages::GetEntities(EMapImageEntityLayerType Entit
bool GameTypeHasTeleLayer = HasTeleLayer(EntitiesModType) || WasUnknwon;
bool GameTypeHasTuneLayer = HasTuneLayer(EntitiesModType) || WasUnknwon;
int TextureLoadFlag = Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
int TextureLoadFlag = 0;
if(Graphics()->IsTileBufferingEnabled())
TextureLoadFlag = (Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE) | IGraphics::TEXLOAD_NO_2D_TEXTURE;
CImageInfo ImgInfo;
bool ImagePNGLoaded = false;
@ -223,7 +225,7 @@ IGraphics::CTextureHandle CMapImages::GetEntities(EMapImageEntityLayerType Entit
if(ImagePNGLoaded && ImgInfo.m_Width > 0 && ImgInfo.m_Height > 0)
{
int ColorChannelCount = 4;
if(ImgInfo.m_Format == CImageInfo::FORMAT_ALPHA)
if(ImgInfo.m_Format == CImageInfo::FORMAT_SINGLE_COMPONENT)
ColorChannelCount = 1;
else if(ImgInfo.m_Format == CImageInfo::FORMAT_RGB)
ColorChannelCount = 3;

View file

@ -839,11 +839,12 @@ void CMapLayers::OnMapLoad()
}
// first create the buffer object
int BufferObjectIndex = Graphics()->CreateBufferObject(UploadDataSize, pUploadData, true);
int BufferObjectIndex = Graphics()->CreateBufferObject(UploadDataSize, pUploadData, 0, true);
// then create the buffer container
SBufferContainerInfo ContainerInfo;
ContainerInfo.m_Stride = (DoTextureCoords ? (sizeof(float) * 2 + sizeof(vec3)) : 0);
ContainerInfo.m_VertBufferBindingIndex = BufferObjectIndex;
ContainerInfo.m_Attributes.emplace_back();
SBufferContainerInfo::SAttribute *pAttr = &ContainerInfo.m_Attributes.back();
pAttr->m_DataTypeCount = 2;
@ -851,7 +852,6 @@ void CMapLayers::OnMapLoad()
pAttr->m_Normalized = false;
pAttr->m_pOffset = 0;
pAttr->m_FuncType = 0;
pAttr->m_VertBufferBindingIndex = BufferObjectIndex;
if(DoTextureCoords)
{
ContainerInfo.m_Attributes.emplace_back();
@ -861,7 +861,6 @@ void CMapLayers::OnMapLoad()
pAttr->m_Normalized = false;
pAttr->m_pOffset = (void *)(sizeof(vec2));
pAttr->m_FuncType = 0;
pAttr->m_VertBufferBindingIndex = BufferObjectIndex;
}
Visuals.m_BufferContainerIndex = Graphics()->CreateBufferContainer(&ContainerInfo);
@ -944,10 +943,11 @@ void CMapLayers::OnMapLoad()
else
pUploadData = &tmpQuads[0];
// create the buffer object
int BufferObjectIndex = Graphics()->CreateBufferObject(UploadDataSize, pUploadData);
int BufferObjectIndex = Graphics()->CreateBufferObject(UploadDataSize, pUploadData, 0);
// then create the buffer container
SBufferContainerInfo ContainerInfo;
ContainerInfo.m_Stride = (Textured ? (sizeof(STmpQuadTextured) / 4) : (sizeof(STmpQuad) / 4));
ContainerInfo.m_VertBufferBindingIndex = BufferObjectIndex;
ContainerInfo.m_Attributes.emplace_back();
SBufferContainerInfo::SAttribute *pAttr = &ContainerInfo.m_Attributes.back();
pAttr->m_DataTypeCount = 4;
@ -955,7 +955,6 @@ void CMapLayers::OnMapLoad()
pAttr->m_Normalized = false;
pAttr->m_pOffset = 0;
pAttr->m_FuncType = 0;
pAttr->m_VertBufferBindingIndex = BufferObjectIndex;
ContainerInfo.m_Attributes.emplace_back();
pAttr = &ContainerInfo.m_Attributes.back();
pAttr->m_DataTypeCount = 4;
@ -963,7 +962,6 @@ void CMapLayers::OnMapLoad()
pAttr->m_Normalized = true;
pAttr->m_pOffset = (void *)(sizeof(float) * 4);
pAttr->m_FuncType = 0;
pAttr->m_VertBufferBindingIndex = BufferObjectIndex;
if(Textured)
{
ContainerInfo.m_Attributes.emplace_back();
@ -973,7 +971,6 @@ void CMapLayers::OnMapLoad()
pAttr->m_Normalized = false;
pAttr->m_pOffset = (void *)(sizeof(float) * 4 + sizeof(unsigned char) * 4);
pAttr->m_FuncType = 0;
pAttr->m_VertBufferBindingIndex = BufferObjectIndex;
}
pQLayerVisuals->m_BufferContainerIndex = Graphics()->CreateBufferContainer(&ContainerInfo);
@ -1406,6 +1403,17 @@ void CMapLayers::RenderQuadLayer(int LayerIndex, CMapItemLayerQuads *pQuadLayer,
Rot = aChannels[2] / 180.0f * pi;
}
bool NeedsFlush = QuadsRenderCount == gs_GraphicsMaxQuadsRenderCount || !(aColor[3] > 0);
if(NeedsFlush)
{
// render quads of the current offset directly(cancel batching)
Graphics()->RenderQuadLayer(Visuals.m_BufferContainerIndex, &s_QuadRenderInfo[0], QuadsRenderCount, CurQuadOffset);
QuadsRenderCount = 0;
// since this quad is ignored, the offset is the next quad
CurQuadOffset = i + 1;
}
if(aColor[3] > 0)
{
SQuadRenderInfo &QInfo = s_QuadRenderInfo[QuadsRenderCount++];
@ -1414,14 +1422,6 @@ void CMapLayers::RenderQuadLayer(int LayerIndex, CMapItemLayerQuads *pQuadLayer,
QInfo.m_aOffsets[1] = OffsetY;
QInfo.m_Rotation = Rot;
}
else
{
// render quads of the current offset directly(cancel batching)
Graphics()->RenderQuadLayer(Visuals.m_BufferContainerIndex, &s_QuadRenderInfo[0], QuadsRenderCount, CurQuadOffset);
QuadsRenderCount = 0;
// since this quad is ignored, the offset is the next quad
CurQuadOffset = i + 1;
}
}
Graphics()->RenderQuadLayer(Visuals.m_BufferContainerIndex, &s_QuadRenderInfo[0], QuadsRenderCount, CurQuadOffset);
}

View file

@ -24,13 +24,18 @@
#include <game/client/ui.h>
#include <game/localization.h>
#include "base/system.h"
#include "binds.h"
#include "camera.h"
#include "countryflags.h"
#include "menus.h"
#include "skins.h"
#include <memory>
#include <utility>
#include <vector>
#include <array>
CMenusKeyBinder CMenus::m_Binder;
@ -1097,9 +1102,7 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
static CVideoMode s_aModes[MAX_RESOLUTIONS];
static int s_NumNodes = Graphics()->GetVideoModes(s_aModes, MAX_RESOLUTIONS, g_Config.m_GfxScreen);
static int s_GfxFsaaSamples = g_Config.m_GfxFsaaSamples;
static int s_GfxOpenGLVersion = Graphics()->IsConfigModernAPI();
static int s_GfxEnableTextureUnitOptimization = g_Config.m_GfxEnableTextureUnitOptimization;
static int s_GfxUsePreinitBuffer = g_Config.m_GfxUsePreinitBuffer;
static bool s_GfxBackendChanged = false;
static int s_GfxHighdpi = g_Config.m_GfxHighdpi;
static int s_InitDisplayAllVideoModes = g_Config.m_GfxDisplayAllVideoModes;
@ -1182,6 +1185,8 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
aWindowModeIDs[i] = &s_aWindowModeIDs[i];
static int s_WindowModeDropDownState = 0;
static int s_OldSelectedBackend = -1;
OldSelected = (g_Config.m_GfxFullscreen ? (g_Config.m_GfxFullscreen == 1 ? 4 : (g_Config.m_GfxFullscreen == 2 ? 3 : 2)) : (g_Config.m_GfxBorderless ? 1 : 0));
const int NewWindowMode = RenderDropDown(s_WindowModeDropDownState, &MainView, OldSelected, aWindowModeIDs, pWindowModes, s_NumWindowMode, &s_NumWindowMode, s_ScrollValueDrop);
@ -1211,12 +1216,12 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
int NumScreens = Graphics()->GetNumScreens();
MainView.HSplitTop(20.0f, &Button, &MainView);
int Screen_MouseButton = DoButton_CheckBox_Number(&g_Config.m_GfxScreen, Localize("Screen"), g_Config.m_GfxScreen, &Button);
if(Screen_MouseButton == 1) //inc
if(Screen_MouseButton == 1) // inc
{
Client()->SwitchWindowScreen((g_Config.m_GfxScreen + 1) % NumScreens);
s_NumNodes = Graphics()->GetVideoModes(s_aModes, MAX_RESOLUTIONS, g_Config.m_GfxScreen);
}
else if(Screen_MouseButton == 2) //dec
else if(Screen_MouseButton == 2) // dec
{
Client()->SwitchWindowScreen((g_Config.m_GfxScreen - 1 + NumScreens) % NumScreens);
s_NumNodes = Graphics()->GetVideoModes(s_aModes, MAX_RESOLUTIONS, g_Config.m_GfxScreen);
@ -1226,12 +1231,12 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
MainView.HSplitTop(20.0f, &Button, &MainView);
str_format(aBuf, sizeof(aBuf), "%s (%s)", Localize("FSAA samples"), Localize("may cause delay"));
int GfxFsaaSamples_MouseButton = DoButton_CheckBox_Number(&g_Config.m_GfxFsaaSamples, aBuf, g_Config.m_GfxFsaaSamples, &Button);
if(GfxFsaaSamples_MouseButton == 1) //inc
if(GfxFsaaSamples_MouseButton == 1) // inc
{
g_Config.m_GfxFsaaSamples = (g_Config.m_GfxFsaaSamples + 1) % 17;
CheckSettings = true;
}
else if(GfxFsaaSamples_MouseButton == 2) //dec
else if(GfxFsaaSamples_MouseButton == 2) // dec
{
g_Config.m_GfxFsaaSamples = (g_Config.m_GfxFsaaSamples - 1 + 17) % 17;
CheckSettings = true;
@ -1241,46 +1246,6 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
if(DoButton_CheckBox(&g_Config.m_GfxHighDetail, Localize("High Detail"), g_Config.m_GfxHighDetail, &Button))
g_Config.m_GfxHighDetail ^= 1;
bool IsNewOpenGL = Graphics()->IsConfigModernAPI();
// only promote modern GL in menu settings if the driver isn't on the blocklist already
if(g_Config.m_GfxDriverIsBlocked == 0)
{
MainView.HSplitTop(20.0f, &Button, &MainView);
if(DoButton_CheckBox(&g_Config.m_GfxOpenGLMajor, Localize("Use modern OpenGL"), IsNewOpenGL, &Button))
{
CheckSettings = true;
if(IsNewOpenGL)
{
Graphics()->GetDriverVersion(GRAPHICS_DRIVER_AGE_TYPE_DEFAULT, g_Config.m_GfxOpenGLMajor, g_Config.m_GfxOpenGLMinor, g_Config.m_GfxOpenGLPatch);
IsNewOpenGL = false;
}
else
{
Graphics()->GetDriverVersion(GRAPHICS_DRIVER_AGE_TYPE_MODERN, g_Config.m_GfxOpenGLMajor, g_Config.m_GfxOpenGLMinor, g_Config.m_GfxOpenGLPatch);
IsNewOpenGL = true;
}
}
if(IsNewOpenGL)
{
MainView.HSplitTop(20.0f, &Button, &MainView);
if(DoButton_CheckBox(&g_Config.m_GfxUsePreinitBuffer, Localize("Preinit VBO (iGPUs only)"), g_Config.m_GfxUsePreinitBuffer, &Button))
{
CheckSettings = true;
g_Config.m_GfxUsePreinitBuffer ^= 1;
}
MainView.HSplitTop(20.0f, &Button, &MainView);
if(DoButton_CheckBox(&g_Config.m_GfxEnableTextureUnitOptimization, Localize("Multiple texture units (disable for macOS)"), g_Config.m_GfxEnableTextureUnitOptimization, &Button))
{
CheckSettings = true;
g_Config.m_GfxEnableTextureUnitOptimization ^= 1;
}
}
}
MainView.HSplitTop(20.0f, &Button, &MainView);
if(DoButton_CheckBox(&g_Config.m_GfxHighdpi, Localize("Use high DPI"), g_Config.m_GfxHighdpi, &Button))
{
@ -1288,16 +1253,6 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
g_Config.m_GfxHighdpi ^= 1;
}
// check if the new settings require a restart
if(CheckSettings)
{
m_NeedRestartGraphics = !(s_GfxFsaaSamples == g_Config.m_GfxFsaaSamples &&
s_GfxOpenGLVersion == (int)IsNewOpenGL &&
s_GfxUsePreinitBuffer == g_Config.m_GfxUsePreinitBuffer &&
s_GfxEnableTextureUnitOptimization == g_Config.m_GfxEnableTextureUnitOptimization &&
s_GfxHighdpi == g_Config.m_GfxHighdpi);
}
MainView.HSplitTop(20.0f, &Label, &MainView);
Label.VSplitLeft(160.0f, &Label, &Button);
if(g_Config.m_GfxRefreshRate)
@ -1314,7 +1269,208 @@ void CMenus::RenderSettingsGraphics(CUIRect MainView)
MainView.HSplitTop(20.0f, &Text, &MainView);
//text.VSplitLeft(15.0f, 0, &text);
UI()->DoLabelScaled(&Text, Localize("UI Color"), 14.0f, TEXTALIGN_LEFT);
RenderHSLScrollbars(&MainView, &g_Config.m_UiColor, true);
CUIRect HSLBar = MainView;
RenderHSLScrollbars(&HSLBar, &g_Config.m_UiColor, true);
MainView.y = HSLBar.y;
MainView.h = MainView.h - MainView.y;
// Backend list
struct SMenuBackendInfo
{
int m_Major = 0;
int m_Minor = 0;
int m_Patch = 0;
const char *m_pBackendName = "";
bool m_Found = false;
};
std::array<std::array<SMenuBackendInfo, EGraphicsDriverAgeType::GRAPHICS_DRIVER_AGE_TYPE_COUNT>, EBackendType::BACKEND_TYPE_COUNT> aaSupportedBackends{};
uint32_t FoundBackendCount = 0;
for(uint32_t i = 0; i < BACKEND_TYPE_COUNT; ++i)
{
if(EBackendType(i) == BACKEND_TYPE_AUTO)
continue;
for(uint32_t n = 0; n < GRAPHICS_DRIVER_AGE_TYPE_COUNT; ++n)
{
auto &Info = aaSupportedBackends[i][n];
if(Graphics()->GetDriverVersion(EGraphicsDriverAgeType(n), Info.m_Major, Info.m_Minor, Info.m_Patch, Info.m_pBackendName, EBackendType(i)))
{
// don't count blocked opengl drivers
if(EBackendType(i) != BACKEND_TYPE_OPENGL || EGraphicsDriverAgeType(n) == GRAPHICS_DRIVER_AGE_TYPE_LEGACY || g_Config.m_GfxDriverIsBlocked == 0)
{
Info.m_Found = true;
++FoundBackendCount;
}
}
}
}
if(FoundBackendCount > 1)
{
MainView.HSplitTop(10.0f, nullptr, &MainView);
MainView.HSplitTop(20.0f, &Text, &MainView);
UI()->DoLabelScaled(&Text, Localize("Renderer"), 16.0f, TEXTALIGN_CENTER);
static float s_ScrollValueDropBackend = 0;
static int s_BackendDropDownState = 0;
static std::vector<std::unique_ptr<int>> vBackendIDs;
static std::vector<const void *> vBackendIDPtrs;
static std::vector<std::string> vBackendIDNames;
static std::vector<const char *> vBackendIDNamesCStr;
static std::vector<SMenuBackendInfo> vBackendInfos;
size_t BackendCount = FoundBackendCount + 1;
vBackendIDs.resize(BackendCount);
vBackendIDPtrs.resize(BackendCount);
vBackendIDNames.resize(BackendCount);
vBackendIDNamesCStr.resize(BackendCount);
vBackendInfos.resize(BackendCount);
char aTmpBackendName[256];
auto IsInfoDefault = [](const SMenuBackendInfo &CheckInfo) {
return str_comp_nocase(CheckInfo.m_pBackendName, "OpenGL") == 0 && CheckInfo.m_Major == 3 && CheckInfo.m_Minor == 0 && CheckInfo.m_Patch == 0;
};
int OldSelectedBackend = -1;
uint32_t CurCounter = 0;
for(uint32_t i = 0; i < BACKEND_TYPE_COUNT; ++i)
{
for(uint32_t n = 0; n < GRAPHICS_DRIVER_AGE_TYPE_COUNT; ++n)
{
auto &Info = aaSupportedBackends[i][n];
if(Info.m_Found)
{
if(vBackendIDs[CurCounter].get() == nullptr)
vBackendIDs[CurCounter] = std::make_unique<int>();
vBackendIDPtrs[CurCounter] = vBackendIDs[CurCounter].get();
{
bool IsDefault = IsInfoDefault(Info);
str_format(aTmpBackendName, sizeof(aTmpBackendName), "%s (%d.%d.%d)%s%s", Info.m_pBackendName, Info.m_Major, Info.m_Minor, Info.m_Patch, IsDefault ? " - " : "", IsDefault ? Localize("default") : "");
vBackendIDNames[CurCounter] = aTmpBackendName;
vBackendIDNamesCStr[CurCounter] = vBackendIDNames[CurCounter].c_str();
if(str_comp_nocase(Info.m_pBackendName, g_Config.m_GfxBackend) == 0 && g_Config.m_GfxGLMajor == Info.m_Major && g_Config.m_GfxGLMinor == Info.m_Minor && g_Config.m_GfxGLPatch == Info.m_Patch)
{
OldSelectedBackend = CurCounter;
}
vBackendInfos[CurCounter] = Info;
}
++CurCounter;
}
}
}
if(OldSelectedBackend != -1)
{
// no custom selected
BackendCount -= 1;
}
else
{
// custom selected one
if(vBackendIDs[CurCounter].get() == nullptr)
vBackendIDs[CurCounter] = std::make_unique<int>();
vBackendIDPtrs[CurCounter] = vBackendIDs[CurCounter].get();
str_format(aTmpBackendName, sizeof(aTmpBackendName), "%s (%s %d.%d.%d)", Localize("custom"), g_Config.m_GfxBackend, g_Config.m_GfxGLMajor, g_Config.m_GfxGLMinor, g_Config.m_GfxGLPatch);
vBackendIDNames[CurCounter] = aTmpBackendName;
vBackendIDNamesCStr[CurCounter] = vBackendIDNames[CurCounter].c_str();
OldSelectedBackend = CurCounter;
vBackendInfos[CurCounter].m_pBackendName = "custom";
vBackendInfos[CurCounter].m_Major = g_Config.m_GfxGLMajor;
vBackendInfos[CurCounter].m_Minor = g_Config.m_GfxGLMinor;
vBackendInfos[CurCounter].m_Patch = g_Config.m_GfxGLPatch;
}
if(s_OldSelectedBackend == -1)
s_OldSelectedBackend = OldSelectedBackend;
static int s_BackendCount = 0;
s_BackendCount = BackendCount;
const int NewBackend = RenderDropDown(s_BackendDropDownState, &MainView, OldSelectedBackend, vBackendIDPtrs.data(), vBackendIDNamesCStr.data(), s_BackendCount, &s_BackendCount, s_ScrollValueDropBackend);
if(OldSelectedBackend != NewBackend)
{
str_copy(g_Config.m_GfxBackend, vBackendInfos[NewBackend].m_pBackendName, sizeof(g_Config.m_GfxBackend));
g_Config.m_GfxGLMajor = vBackendInfos[NewBackend].m_Major;
g_Config.m_GfxGLMinor = vBackendInfos[NewBackend].m_Minor;
g_Config.m_GfxGLPatch = vBackendInfos[NewBackend].m_Patch;
CheckSettings = true;
s_GfxBackendChanged = s_OldSelectedBackend != NewBackend;
}
}
// GPU list
const auto &GPUList = Graphics()->GetGPUs();
if(GPUList.m_GPUs.size() > 1)
{
MainView.HSplitTop(10.0f, nullptr, &MainView);
MainView.HSplitTop(20.0f, &Text, &MainView);
UI()->DoLabelScaled(&Text, Localize("Graphics cards"), 16.0f, TEXTALIGN_CENTER);
static float s_ScrollValueDropGPU = 0;
static int s_GPUDropDownState = 0;
static std::vector<std::unique_ptr<int>> vGPUIDs;
static std::vector<const void *> vGPUIDPtrs;
static std::vector<const char *> vGPUIDNames;
size_t GPUCount = GPUList.m_GPUs.size() + 1;
vGPUIDs.resize(GPUCount);
vGPUIDPtrs.resize(GPUCount);
vGPUIDNames.resize(GPUCount);
char aCurDeviceName[256 + 4];
int OldSelectedGPU = -1;
for(size_t i = 0; i < GPUCount; ++i)
{
if(vGPUIDs[i].get() == nullptr)
vGPUIDs[i] = std::make_unique<int>();
vGPUIDPtrs[i] = vGPUIDs[i].get();
if(i == 0)
{
str_format(aCurDeviceName, sizeof(aCurDeviceName), "%s(%s)", Localize("auto"), GPUList.m_AutoGPU.m_Name);
vGPUIDNames[i] = aCurDeviceName;
if(str_comp("auto", g_Config.m_GfxGPUName) == 0)
{
OldSelectedGPU = 0;
}
}
else
{
vGPUIDNames[i] = GPUList.m_GPUs[i - 1].m_Name;
if(str_comp(GPUList.m_GPUs[i - 1].m_Name, g_Config.m_GfxGPUName) == 0)
{
OldSelectedGPU = i;
}
}
}
static int s_GPUCount = 0;
s_GPUCount = GPUCount;
const int NewGPU = RenderDropDown(s_GPUDropDownState, &MainView, OldSelectedGPU, vGPUIDPtrs.data(), vGPUIDNames.data(), s_GPUCount, &s_GPUCount, s_ScrollValueDropGPU);
if(OldSelectedGPU != NewGPU)
{
if(NewGPU == 0)
str_copy(g_Config.m_GfxGPUName, "auto", sizeof(g_Config.m_GfxGPUName));
else
str_copy(g_Config.m_GfxGPUName, GPUList.m_GPUs[NewGPU - 1].m_Name, sizeof(g_Config.m_GfxGPUName));
}
}
// check if the new settings require a restart
if(CheckSettings)
{
m_NeedRestartGraphics = !(s_GfxFsaaSamples == g_Config.m_GfxFsaaSamples &&
!s_GfxBackendChanged &&
s_GfxHighdpi == g_Config.m_GfxHighdpi);
}
}
void CMenus::RenderSettingsSound(CUIRect MainView)

View file

@ -231,7 +231,7 @@ void CParticles::RenderGroup(int Group)
// the current position, respecting the size, is inside the viewport, render it, else ignore
if(ParticleIsVisibleOnScreen(p, Size))
{
if(LastColor[0] != m_aParticles[i].m_Color.r || LastColor[1] != m_aParticles[i].m_Color.g || LastColor[2] != m_aParticles[i].m_Color.b || LastColor[3] != m_aParticles[i].m_Color.a || LastQuadOffset != QuadOffset)
if((size_t)CurParticleRenderCount == gs_GraphicsMaxParticlesRenderCount || LastColor[0] != m_aParticles[i].m_Color.r || LastColor[1] != m_aParticles[i].m_Color.g || LastColor[2] != m_aParticles[i].m_Color.b || LastColor[3] != m_aParticles[i].m_Color.a || LastQuadOffset != QuadOffset)
{
Graphics()->TextureSet(GameClient()->m_ParticlesSkin.m_SpriteParticles[LastQuadOffset - SPRITE_PART_SLICE]);
Graphics()->RenderQuadContainerAsSpriteMultiple(m_ParticleQuadContainerIndex, LastQuadOffset, CurParticleRenderCount, s_aParticleRenderInfo);

View file

@ -5666,7 +5666,7 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
void CEditor::Render()
{
// basic start
Graphics()->Clear(1.0f, 0.0f, 1.0f);
Graphics()->Clear(0.0f, 0.0f, 0.0f);
CUIRect View = *UI()->Screen();
UI()->MapScreen();
@ -6031,7 +6031,10 @@ void CEditor::Reset(bool CreateDefault)
// create default layers
if(CreateDefault)
m_Map.CreateDefault(m_EntitiesTexture);
{
m_EditorWasUsedBefore = true;
m_Map.CreateDefault(GetEntitiesTexture());
}
SelectGameLayer();
m_lSelectedQuads.clear();
@ -6141,7 +6144,7 @@ void CEditorMap::MakeGameLayer(CLayer *pLayer)
{
m_pGameLayer = (CLayerGame *)pLayer;
m_pGameLayer->m_pEditor = m_pEditor;
m_pGameLayer->m_Texture = m_pEditor->m_EntitiesTexture;
m_pGameLayer->m_Texture = m_pEditor->GetEntitiesTexture();
}
void CEditorMap::MakeGameGroup(CLayerGroup *pGroup)
@ -6207,6 +6210,60 @@ void CEditorMap::CreateDefault(IGraphics::CTextureHandle EntitiesTexture)
m_pTuneLayer = 0x0;
}
int CEditor::GetTextureUsageFlag()
{
return Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
}
IGraphics::CTextureHandle CEditor::GetFrontTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_FrontTexture.IsValid())
m_FrontTexture = Graphics()->LoadTexture("editor/front.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_FrontTexture;
}
IGraphics::CTextureHandle CEditor::GetTeleTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_TeleTexture.IsValid())
m_TeleTexture = Graphics()->LoadTexture("editor/tele.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_TeleTexture;
}
IGraphics::CTextureHandle CEditor::GetSpeedupTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_SpeedupTexture.IsValid())
m_SpeedupTexture = Graphics()->LoadTexture("editor/speedup.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_SpeedupTexture;
}
IGraphics::CTextureHandle CEditor::GetSwitchTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_SwitchTexture.IsValid())
m_SwitchTexture = Graphics()->LoadTexture("editor/switch.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_SwitchTexture;
}
IGraphics::CTextureHandle CEditor::GetTuneTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_TuneTexture.IsValid())
m_TuneTexture = Graphics()->LoadTexture("editor/tune.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_TuneTexture;
}
IGraphics::CTextureHandle CEditor::GetEntitiesTexture()
{
int TextureLoadFlag = GetTextureUsageFlag();
if(!m_EntitiesTexture.IsValid())
m_EntitiesTexture = Graphics()->LoadTexture("editor/entities/DDNet.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
return m_EntitiesTexture;
}
void CEditor::Init()
{
m_pInput = Kernel()->RequestInterface<IInput>();
@ -6227,14 +6284,6 @@ void CEditor::Init()
m_CheckerTexture = Graphics()->LoadTexture("editor/checker.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
m_BackgroundTexture = Graphics()->LoadTexture("editor/background.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
m_CursorTexture = Graphics()->LoadTexture("editor/cursor.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, 0);
int TextureLoadFlag = Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
m_EntitiesTexture = Graphics()->LoadTexture("editor/entities/DDNet.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
m_FrontTexture = Graphics()->LoadTexture("editor/front.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
m_TeleTexture = Graphics()->LoadTexture("editor/tele.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
m_SpeedupTexture = Graphics()->LoadTexture("editor/speedup.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
m_SwitchTexture = Graphics()->LoadTexture("editor/switch.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
m_TuneTexture = Graphics()->LoadTexture("editor/tune.png", IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
m_TilesetPicker.m_pEditor = this;
m_TilesetPicker.MakePalette();
@ -6246,7 +6295,7 @@ void CEditor::Init()
m_Brush.m_pMap = &m_Map;
Reset();
Reset(false);
m_Map.m_Modified = false;
ms_PickerColor = ColorHSVA(1.0f, 0.0f, 0.0f);
@ -6274,6 +6323,12 @@ void CEditor::UpdateAndRender()
static float s_MouseX = 0.0f;
static float s_MouseY = 0.0f;
if(!m_EditorWasUsedBefore)
{
m_EditorWasUsedBefore = true;
Reset();
}
if(m_Animate)
m_AnimateTime = (time_get() - m_AnimateStart) / (float)time_freq();
else
@ -6369,33 +6424,33 @@ void CEditorMap::MakeTeleLayer(CLayer *pLayer)
{
m_pTeleLayer = (CLayerTele *)pLayer;
m_pTeleLayer->m_pEditor = m_pEditor;
m_pTeleLayer->m_Texture = m_pEditor->m_TeleTexture;
m_pTeleLayer->m_Texture = m_pEditor->GetTeleTexture();
}
void CEditorMap::MakeSpeedupLayer(CLayer *pLayer)
{
m_pSpeedupLayer = (CLayerSpeedup *)pLayer;
m_pSpeedupLayer->m_pEditor = m_pEditor;
m_pSpeedupLayer->m_Texture = m_pEditor->m_SpeedupTexture;
m_pSpeedupLayer->m_Texture = m_pEditor->GetSpeedupTexture();
}
void CEditorMap::MakeFrontLayer(CLayer *pLayer)
{
m_pFrontLayer = (CLayerFront *)pLayer;
m_pFrontLayer->m_pEditor = m_pEditor;
m_pFrontLayer->m_Texture = m_pEditor->m_FrontTexture;
m_pFrontLayer->m_Texture = m_pEditor->GetFrontTexture();
}
void CEditorMap::MakeSwitchLayer(CLayer *pLayer)
{
m_pSwitchLayer = (CLayerSwitch *)pLayer;
m_pSwitchLayer->m_pEditor = m_pEditor;
m_pSwitchLayer->m_Texture = m_pEditor->m_SwitchTexture;
m_pSwitchLayer->m_Texture = m_pEditor->GetSwitchTexture();
}
void CEditorMap::MakeTuneLayer(CLayer *pLayer)
{
m_pTuneLayer = (CLayerTune *)pLayer;
m_pTuneLayer->m_pEditor = m_pEditor;
m_pTuneLayer->m_Texture = m_pEditor->m_TuneTexture;
m_pTuneLayer->m_Texture = m_pEditor->GetTuneTexture();
}

View file

@ -641,6 +641,18 @@ class CEditor : public IEditor
CUI m_UI;
CUIEx m_UIEx;
bool m_EditorWasUsedBefore = false;
IGraphics::CTextureHandle m_EntitiesTexture;
IGraphics::CTextureHandle m_FrontTexture;
IGraphics::CTextureHandle m_TeleTexture;
IGraphics::CTextureHandle m_SpeedupTexture;
IGraphics::CTextureHandle m_SwitchTexture;
IGraphics::CTextureHandle m_TuneTexture;
int GetTextureUsageFlag();
public:
class IInput *Input() { return m_pInput; }
class IClient *Client() { return m_pClient; }
@ -924,7 +936,8 @@ public:
IGraphics::CTextureHandle m_CheckerTexture;
IGraphics::CTextureHandle m_BackgroundTexture;
IGraphics::CTextureHandle m_CursorTexture;
IGraphics::CTextureHandle m_EntitiesTexture;
IGraphics::CTextureHandle GetEntitiesTexture();
CLayerGroup m_Brush;
CLayerTiles m_TilesetPicker;
@ -1134,11 +1147,12 @@ public:
// DDRace
IGraphics::CTextureHandle m_FrontTexture;
IGraphics::CTextureHandle m_TeleTexture;
IGraphics::CTextureHandle m_SpeedupTexture;
IGraphics::CTextureHandle m_SwitchTexture;
IGraphics::CTextureHandle m_TuneTexture;
IGraphics::CTextureHandle GetFrontTexture();
IGraphics::CTextureHandle GetTeleTexture();
IGraphics::CTextureHandle GetSpeedupTexture();
IGraphics::CTextureHandle GetSwitchTexture();
IGraphics::CTextureHandle GetTuneTexture();
static int PopupTele(CEditor *pEditor, CUIRect View, void *pContext);
static int PopupSpeedup(CEditor *pEditor, CUIRect View, void *pContext);
static int PopupSwitch(CEditor *pEditor, CUIRect View, void *pContext);

View file

@ -1880,8 +1880,9 @@ int CEditor::PopupEntities(CEditor *pEditor, CUIRect View, void *pContext)
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "editor/entities/%s.png", Name);
pEditor->Graphics()->UnloadTexture(&pEditor->m_EntitiesTexture);
int TextureLoadFlag = pEditor->Graphics()->HasTextureArrays() ? IGraphics::TEXLOAD_TO_2D_ARRAY_TEXTURE : IGraphics::TEXLOAD_TO_3D_TEXTURE;
if(pEditor->m_EntitiesTexture.IsValid())
pEditor->Graphics()->UnloadTexture(&pEditor->m_EntitiesTexture);
int TextureLoadFlag = pEditor->GetTextureUsageFlag();
pEditor->m_EntitiesTexture = pEditor->Graphics()->LoadTexture(aBuf, IStorage::TYPE_ALL, CImageInfo::FORMAT_AUTO, TextureLoadFlag);
g_UiNumPopups--;
}