Compare commits
13 Commits
temp-gpu-i
...
draw-viewp
Author | SHA1 | Date | |
---|---|---|---|
0bdf574ea2 | |||
af87b6d8cb | |||
42d2c96d4c | |||
941fdefdb3 | |||
a0df3c4d51 | |||
4984cba10d | |||
59a0099b9f | |||
f8cfd7e288 | |||
dc0c074ac4 | |||
0053d2fc81 | |||
e6d94b83ba | |||
81632de706 | |||
f7cb19956f |
@@ -31,7 +31,7 @@ ExternalProject_Add(external_python_site_packages
|
||||
CONFIGURE_COMMAND ${PIP_CONFIGURE_COMMAND}
|
||||
BUILD_COMMAND ""
|
||||
PREFIX ${BUILD_DIR}/site_packages
|
||||
INSTALL_COMMAND ${PYTHON_BINARY} -m pip install --no-cache-dir ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all:
|
||||
INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all:
|
||||
)
|
||||
|
||||
if(USE_PIP_NUMPY)
|
||||
|
@@ -189,11 +189,11 @@ set(OSL_HASH 1abd7ce40481771a9fa937f19595d2f2)
|
||||
set(OSL_HASH_TYPE MD5)
|
||||
set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz)
|
||||
|
||||
set(PYTHON_VERSION 3.10.2)
|
||||
set(PYTHON_SHORT_VERSION 3.10)
|
||||
set(PYTHON_SHORT_VERSION_NO_DOTS 310)
|
||||
set(PYTHON_VERSION 3.9.7)
|
||||
set(PYTHON_SHORT_VERSION 3.9)
|
||||
set(PYTHON_SHORT_VERSION_NO_DOTS 39)
|
||||
set(PYTHON_URI https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz)
|
||||
set(PYTHON_HASH 14e8c22458ed7779a1957b26cde01db9)
|
||||
set(PYTHON_HASH fddb060b483bc01850a3f412eea1d954)
|
||||
set(PYTHON_HASH_TYPE MD5)
|
||||
set(PYTHON_FILE Python-${PYTHON_VERSION}.tar.xz)
|
||||
|
||||
@@ -215,20 +215,18 @@ set(NANOVDB_HASH e7b9e863ec2f3b04ead171dec2322807)
|
||||
set(NANOVDB_HASH_TYPE MD5)
|
||||
set(NANOVDB_FILE nano-vdb-${NANOVDB_GIT_UID}.tar.gz)
|
||||
|
||||
set(IDNA_VERSION 3.3)
|
||||
set(CHARSET_NORMALIZER_VERSION 2.0.10)
|
||||
set(URLLIB3_VERSION 1.26.8)
|
||||
set(IDNA_VERSION 3.2)
|
||||
set(CHARSET_NORMALIZER_VERSION 2.0.6)
|
||||
set(URLLIB3_VERSION 1.26.7)
|
||||
set(CERTIFI_VERSION 2021.10.8)
|
||||
set(REQUESTS_VERSION 2.27.1)
|
||||
set(CYTHON_VERSION 0.29.26)
|
||||
# The version of the zstd library used to build the Python package should match ZSTD_VERSION defined below.
|
||||
# At this time of writing, 0.17.0 was already released, but built against zstd 1.5.1, while we use 1.5.0.
|
||||
set(ZSTANDARD_VERSION 0.16.0)
|
||||
set(REQUESTS_VERSION 2.26.0)
|
||||
set(CYTHON_VERSION 0.29.24)
|
||||
set(ZSTANDARD_VERSION 0.15.2 )
|
||||
|
||||
set(NUMPY_VERSION 1.22.0)
|
||||
set(NUMPY_SHORT_VERSION 1.22)
|
||||
set(NUMPY_VERSION 1.21.2)
|
||||
set(NUMPY_SHORT_VERSION 1.21)
|
||||
set(NUMPY_URI https://github.com/numpy/numpy/releases/download/v${NUMPY_VERSION}/numpy-${NUMPY_VERSION}.zip)
|
||||
set(NUMPY_HASH 252de134862a27bd66705d29622edbfe)
|
||||
set(NUMPY_HASH 5638d5dae3ca387be562912312db842e)
|
||||
set(NUMPY_HASH_TYPE MD5)
|
||||
set(NUMPY_FILE numpy-${NUMPY_VERSION}.zip)
|
||||
|
||||
|
@@ -379,27 +379,27 @@ USE_CXX11=true
|
||||
CLANG_FORMAT_VERSION_MIN="6.0"
|
||||
CLANG_FORMAT_VERSION_MEX="10.0"
|
||||
|
||||
PYTHON_VERSION="3.10.2"
|
||||
PYTHON_VERSION_SHORT="3.10"
|
||||
PYTHON_VERSION_MIN="3.9"
|
||||
PYTHON_VERSION_MEX="3.12"
|
||||
PYTHON_VERSION="3.9.7"
|
||||
PYTHON_VERSION_SHORT="3.9"
|
||||
PYTHON_VERSION_MIN="3.7"
|
||||
PYTHON_VERSION_MEX="3.11"
|
||||
PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT
|
||||
PYTHON_FORCE_BUILD=false
|
||||
PYTHON_FORCE_REBUILD=false
|
||||
PYTHON_SKIP=false
|
||||
|
||||
# Additional Python modules.
|
||||
PYTHON_IDNA_VERSION="3.3"
|
||||
PYTHON_IDNA_VERSION="3.2"
|
||||
PYTHON_IDNA_VERSION_MIN="2.0"
|
||||
PYTHON_IDNA_VERSION_MEX="4.0"
|
||||
PYTHON_IDNA_NAME="idna"
|
||||
|
||||
PYTHON_CHARSET_NORMALIZER_VERSION="2.0.10"
|
||||
PYTHON_CHARSET_NORMALIZER_VERSION="2.0.6"
|
||||
PYTHON_CHARSET_NORMALIZER_VERSION_MIN="2.0.6"
|
||||
PYTHON_CHARSET_NORMALIZER_VERSION_MEX="2.1.0" # requests uses `charset_normalizer~=2.0.0`
|
||||
PYTHON_CHARSET_NORMALIZER_NAME="charset-normalizer"
|
||||
|
||||
PYTHON_URLLIB3_VERSION="1.26.8"
|
||||
PYTHON_URLLIB3_VERSION="1.26.7"
|
||||
PYTHON_URLLIB3_VERSION_MIN="1.0"
|
||||
PYTHON_URLLIB3_VERSION_MEX="2.0"
|
||||
PYTHON_URLLIB3_NAME="urllib3"
|
||||
@@ -409,17 +409,17 @@ PYTHON_CERTIFI_VERSION_MIN="2021.0"
|
||||
PYTHON_CERTIFI_VERSION_MEX="2023.0"
|
||||
PYTHON_CERTIFI_NAME="certifi"
|
||||
|
||||
PYTHON_REQUESTS_VERSION="2.27.1"
|
||||
PYTHON_REQUESTS_VERSION="2.23.0"
|
||||
PYTHON_REQUESTS_VERSION_MIN="2.0"
|
||||
PYTHON_REQUESTS_VERSION_MEX="3.0"
|
||||
PYTHON_REQUESTS_NAME="requests"
|
||||
|
||||
PYTHON_ZSTANDARD_VERSION="0.16.0"
|
||||
PYTHON_ZSTANDARD_VERSION="0.15.2"
|
||||
PYTHON_ZSTANDARD_VERSION_MIN="0.15.2"
|
||||
PYTHON_ZSTANDARD_VERSION_MEX="0.20.0"
|
||||
PYTHON_ZSTANDARD_VERSION_MEX="0.16.0"
|
||||
PYTHON_ZSTANDARD_NAME="zstandard"
|
||||
|
||||
PYTHON_NUMPY_VERSION="1.22.0"
|
||||
PYTHON_NUMPY_VERSION="1.21.2"
|
||||
PYTHON_NUMPY_VERSION_MIN="1.14"
|
||||
PYTHON_NUMPY_VERSION_MEX="2.0"
|
||||
PYTHON_NUMPY_NAME="numpy"
|
||||
@@ -499,7 +499,7 @@ LLVM_FORCE_REBUILD=false
|
||||
LLVM_SKIP=false
|
||||
|
||||
# OSL needs to be compiled for now!
|
||||
OSL_VERSION="1.11.17.0"
|
||||
OSL_VERSION="1.11.14.1"
|
||||
OSL_VERSION_SHORT="1.11"
|
||||
OSL_VERSION_MIN="1.11"
|
||||
OSL_VERSION_MEX="2.0"
|
||||
|
@@ -128,20 +128,25 @@ if(WITH_CODEC_SNDFILE)
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON)
|
||||
# Use precompiled libraries by default.
|
||||
set(PYTHON_VERSION 3.10)
|
||||
# we use precompiled libraries for py 3.9 and up by default
|
||||
set(PYTHON_VERSION 3.9)
|
||||
if(NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK)
|
||||
# Normally cached but not since we include them with blender.
|
||||
# normally cached but not since we include them with blender
|
||||
set(PYTHON_INCLUDE_DIR "${LIBDIR}/python/include/python${PYTHON_VERSION}")
|
||||
set(PYTHON_EXECUTABLE "${LIBDIR}/python/bin/python${PYTHON_VERSION}")
|
||||
set(PYTHON_LIBRARY ${LIBDIR}/python/lib/libpython${PYTHON_VERSION}.a)
|
||||
set(PYTHON_LIBPATH "${LIBDIR}/python/lib/python${PYTHON_VERSION}")
|
||||
# set(PYTHON_LINKFLAGS "-u _PyMac_Error") # won't build with this enabled
|
||||
else()
|
||||
# Module must be compiled against Python framework.
|
||||
# module must be compiled against Python framework
|
||||
set(_py_framework "/Library/Frameworks/Python.framework/Versions/${PYTHON_VERSION}")
|
||||
|
||||
set(PYTHON_INCLUDE_DIR "${_py_framework}/include/python${PYTHON_VERSION}")
|
||||
set(PYTHON_EXECUTABLE "${_py_framework}/bin/python${PYTHON_VERSION}")
|
||||
set(PYTHON_LIBPATH "${_py_framework}/lib/python${PYTHON_VERSION}")
|
||||
# set(PYTHON_LIBRARY python${PYTHON_VERSION})
|
||||
# set(PYTHON_LINKFLAGS "-u _PyMac_Error -framework Python") # won't build with this enabled
|
||||
|
||||
unset(_py_framework)
|
||||
endif()
|
||||
|
||||
|
@@ -55,10 +55,6 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
message(WARNING "stripped pdb not supported with clang, disabling..")
|
||||
set(WITH_WINDOWS_STRIPPED_PDB OFF)
|
||||
endif()
|
||||
else()
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.28.29921) # MSVC 2019 16.9.16
|
||||
message(FATAL_ERROR "Compiler is unsupported, MSVC 2019 16.9.16 or newer is required for building blender.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT WITH_PYTHON_MODULE)
|
||||
@@ -269,6 +265,12 @@ if(NOT DEFINED LIBDIR)
|
||||
elseif(MSVC_VERSION GREATER 1919)
|
||||
message(STATUS "Visual Studio 2019 detected.")
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
|
||||
elseif(MSVC_VERSION GREATER 1909)
|
||||
message(STATUS "Visual Studio 2017 detected.")
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
|
||||
elseif(MSVC_VERSION EQUAL 1900)
|
||||
message(STATUS "Visual Studio 2015 detected.")
|
||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/../lib/${LIBDIR_BASE}_vc15)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Using pre-compiled LIBDIR: ${LIBDIR}")
|
||||
@@ -463,7 +465,7 @@ if(WITH_JACK)
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON)
|
||||
set(PYTHON_VERSION 3.10) # CACHE STRING)
|
||||
set(PYTHON_VERSION 3.9) # CACHE STRING)
|
||||
|
||||
string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION})
|
||||
set(PYTHON_LIBRARY ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.lib)
|
||||
|
@@ -3,6 +3,9 @@ echo No explicit msvc version requested, autodetecting version.
|
||||
call "%~dp0\detect_msvc2019.cmd"
|
||||
if %ERRORLEVEL% EQU 0 goto DetectionComplete
|
||||
|
||||
call "%~dp0\detect_msvc2017.cmd"
|
||||
if %ERRORLEVEL% EQU 0 goto DetectionComplete
|
||||
|
||||
call "%~dp0\detect_msvc2022.cmd"
|
||||
if %ERRORLEVEL% EQU 0 goto DetectionComplete
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15
|
||||
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15
|
||||
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
|
||||
|
||||
|
@@ -19,6 +19,12 @@ if "%WITH_PYDEBUG%"=="1" (
|
||||
set PYDEBUG_CMAKE_ARGS=-DWINDOWS_PYTHON_DEBUG=On
|
||||
)
|
||||
|
||||
if "%BUILD_VS_YEAR%"=="2017" (
|
||||
set BUILD_GENERATOR_POST=%WINDOWS_ARCH%
|
||||
) else (
|
||||
set BUILD_PLATFORM_SELECT=-A %MSBUILD_PLATFORM%
|
||||
)
|
||||
|
||||
set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -G "Visual Studio %BUILD_VS_VER% %BUILD_VS_YEAR%%BUILD_GENERATOR_POST%" %BUILD_PLATFORM_SELECT% %TESTS_CMAKE_ARGS% %CLANG_CMAKE_ARGS% %ASAN_CMAKE_ARGS% %PYDEBUG_CMAKE_ARGS%
|
||||
|
||||
if NOT EXIST %BUILD_DIR%\nul (
|
||||
|
@@ -37,9 +37,15 @@ set LLVM_DIR=
|
||||
:DetectionComplete
|
||||
set CC=%LLVM_DIR%\bin\clang-cl
|
||||
set CXX=%LLVM_DIR%\bin\clang-cl
|
||||
rem build and tested against 2019 16.2
|
||||
set CFLAGS=-m64 -fmsc-version=1922
|
||||
set CXXFLAGS=-m64 -fmsc-version=1922
|
||||
if "%BUILD_VS_YEAR%" == "2019" (
|
||||
rem build and tested against 2019 16.2
|
||||
set CFLAGS=-m64 -fmsc-version=1922
|
||||
set CXXFLAGS=-m64 -fmsc-version=1922
|
||||
) else (
|
||||
rem build and tested against 2017 15.7
|
||||
set CFLAGS=-m64 -fmsc-version=1914
|
||||
set CXXFLAGS=-m64 -fmsc-version=1914
|
||||
)
|
||||
)
|
||||
|
||||
if "%WITH_ASAN%"=="1" (
|
||||
|
3
build_files/windows/detect_msvc2017.cmd
Normal file
3
build_files/windows/detect_msvc2017.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
set BUILD_VS_VER=15
|
||||
set BUILD_VS_YEAR=2017
|
||||
call "%~dp0\detect_msvc_vswhere.cmd"
|
@@ -50,6 +50,14 @@ if NOT "%1" == "" (
|
||||
goto ERR
|
||||
) else if "%1" == "x64" (
|
||||
set BUILD_ARCH=x64
|
||||
) else if "%1" == "2017" (
|
||||
set BUILD_VS_YEAR=2017
|
||||
) else if "%1" == "2017pre" (
|
||||
set BUILD_VS_YEAR=2017
|
||||
set VSWHERE_ARGS=-prerelease
|
||||
) else if "%1" == "2017b" (
|
||||
set BUILD_VS_YEAR=2017
|
||||
set VSWHERE_ARGS=-products Microsoft.VisualStudio.Product.BuildTools
|
||||
) else if "%1" == "2019" (
|
||||
set BUILD_VS_YEAR=2019
|
||||
) else if "%1" == "2019pre" (
|
||||
|
@@ -24,12 +24,12 @@ echo - nobuildinfo ^(disable buildinfo^)
|
||||
echo - debug ^(Build an unoptimized debuggable build^)
|
||||
echo - packagename [newname] ^(override default cpack package name^)
|
||||
echo - builddir [newdir] ^(override default build folder^)
|
||||
echo - 2017 ^(build with visual studio 2017^)
|
||||
echo - 2017pre ^(build with visual studio 2017 pre-release^)
|
||||
echo - 2017b ^(build with visual studio 2017 Build Tools^)
|
||||
echo - 2019 ^(build with visual studio 2019^)
|
||||
echo - 2019pre ^(build with visual studio 2019 pre-release^)
|
||||
echo - 2019b ^(build with visual studio 2019 Build Tools^)
|
||||
echo - 2022 ^(build with visual studio 2022^)
|
||||
echo - 2022pre ^(build with visual studio 2022 pre-release^)
|
||||
echo - 2022b ^(build with visual studio 2022 Build Tools^)
|
||||
|
||||
echo.
|
||||
echo Documentation Targets ^(Not associated with building^)
|
||||
|
@@ -1,3 +1,4 @@
|
||||
if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15
|
||||
if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15
|
||||
if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15
|
||||
|
||||
|
@@ -46,7 +46,7 @@ void *BKE_camera_add(struct Main *bmain, const char *name);
|
||||
/**
|
||||
* Get the camera's DOF value, takes the DOF object into account.
|
||||
*/
|
||||
float BKE_camera_object_dof_distance(const struct Object *ob);
|
||||
float BKE_camera_object_dof_distance(struct Object *ob);
|
||||
|
||||
int BKE_camera_sensor_fit(int sensor_fit, float sizex, float sizey);
|
||||
float BKE_camera_sensor_size(int sensor_fit, float sensor_x, float sensor_y);
|
||||
|
@@ -436,12 +436,6 @@ int CustomData_get_render_layer(const struct CustomData *data, int type);
|
||||
int CustomData_get_clone_layer(const struct CustomData *data, int type);
|
||||
int CustomData_get_stencil_layer(const struct CustomData *data, int type);
|
||||
|
||||
/**
|
||||
* Returns name of the active layer of the given type or NULL
|
||||
* if no such active layer is defined.
|
||||
*/
|
||||
const char *CustomData_get_active_layer_name(const struct CustomData *data, int type);
|
||||
|
||||
/**
|
||||
* Copies the data from source to the data element at index in the first layer of type
|
||||
* no effect if there is no layer of type.
|
||||
|
@@ -24,8 +24,6 @@
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -563,7 +561,6 @@ struct GPUTexture *BKE_image_get_gpu_tilemap(struct Image *image,
|
||||
* Is the alpha of the `GPUTexture` for a given image/ibuf premultiplied.
|
||||
*/
|
||||
bool BKE_image_has_gpu_texture_premultiplied_alpha(struct Image *image, struct ImBuf *ibuf);
|
||||
|
||||
/**
|
||||
* Partial update of texture for texture painting.
|
||||
* This is often much quicker than fully updating the texture for high resolution images.
|
||||
@@ -575,14 +572,8 @@ void BKE_image_update_gputexture(
|
||||
* The next time the #GPUTexture is used these tiles will be refreshes. This saves time
|
||||
* when writing to the same place multiple times This happens for during foreground rendering.
|
||||
*/
|
||||
void BKE_image_update_gputexture_delayed(struct Image *ima,
|
||||
struct ImageTile *image_tile,
|
||||
struct ImBuf *ibuf,
|
||||
int x,
|
||||
int y,
|
||||
int w,
|
||||
int h);
|
||||
|
||||
void BKE_image_update_gputexture_delayed(
|
||||
struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h);
|
||||
/**
|
||||
* Called on entering and exiting texture paint mode,
|
||||
* temporary disabling/enabling mipmapping on all images for quick texture
|
||||
@@ -600,32 +591,6 @@ bool BKE_image_remove_renderslot(struct Image *ima, struct ImageUser *iuser, int
|
||||
struct RenderSlot *BKE_image_get_renderslot(struct Image *ima, int index);
|
||||
bool BKE_image_clear_renderslot(struct Image *ima, struct ImageUser *iuser, int slot);
|
||||
|
||||
/* --- image_partial_update.cc --- */
|
||||
/** Image partial updates. */
|
||||
struct PartialUpdateUser;
|
||||
|
||||
/**
|
||||
* \brief Create a new PartialUpdateUser. An Object that contains data to use partial updates.
|
||||
*/
|
||||
struct PartialUpdateUser *BKE_image_partial_update_create(const struct Image *image);
|
||||
|
||||
/**
|
||||
* \brief free a partial update user.
|
||||
*/
|
||||
void BKE_image_partial_update_free(struct PartialUpdateUser *user);
|
||||
|
||||
/* --- partial updater (image side) --- */
|
||||
struct PartialUpdateRegister;
|
||||
|
||||
void BKE_image_partial_update_register_free(struct Image *image);
|
||||
/** \brief Mark a region of the image to update. */
|
||||
void BKE_image_partial_update_mark_region(struct Image *image,
|
||||
const struct ImageTile *image_tile,
|
||||
const struct ImBuf *image_buffer,
|
||||
const rcti *updated_region);
|
||||
/** \brief Mark the whole image to be updated. */
|
||||
void BKE_image_partial_update_mark_full_update(struct Image *image);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* To reduce the overhead of image processing this file contains a mechanism to detect areas of the
|
||||
* image that are changed. These areas are organized in chunks. Changes that happen over time are
|
||||
* organized in changesets.
|
||||
*
|
||||
* A common usecase is to update GPUTexture for drawing where only that part is uploaded that only
|
||||
* changed.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
extern "C" {
|
||||
struct PartialUpdateUser;
|
||||
struct PartialUpdateRegister;
|
||||
}
|
||||
|
||||
namespace blender::bke::image {
|
||||
|
||||
using TileNumber = int;
|
||||
|
||||
namespace partial_update {
|
||||
|
||||
/* --- image_partial_update.cc --- */
|
||||
/** Image partial updates. */
|
||||
|
||||
/**
|
||||
* \brief Result codes of #BKE_image_partial_update_collect_changes.
|
||||
*/
|
||||
enum class ePartialUpdateCollectResult {
|
||||
/** \brief Unable to construct partial updates. Caller should perform a full update. */
|
||||
FullUpdateNeeded,
|
||||
|
||||
/** \brief No changes detected since the last time requested. */
|
||||
NoChangesDetected,
|
||||
|
||||
/** \brief Changes detected since the last time requested. */
|
||||
PartialChangesDetected,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief A region to update.
|
||||
*
|
||||
* Data is organized in tiles. These tiles are in texel space (1 unit is a single texel). When
|
||||
* tiles are requested they are merged with neighboring tiles.
|
||||
*/
|
||||
struct PartialUpdateRegion {
|
||||
/** \brief region of the image that has been updated. Region can be bigger than actual changes.
|
||||
*/
|
||||
struct rcti region;
|
||||
|
||||
/**
|
||||
* \brief Tile number (UDIM) that this region belongs to.
|
||||
*/
|
||||
TileNumber tile_number;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return codes of #BKE_image_partial_update_get_next_change.
|
||||
*/
|
||||
enum class ePartialUpdateIterResult {
|
||||
/** \brief no tiles left when iterating over tiles. */
|
||||
Finished = 0,
|
||||
|
||||
/** \brief a chunk was available and has been loaded. */
|
||||
ChangeAvailable = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief collect the partial update since the last request.
|
||||
*
|
||||
* Invoke #BKE_image_partial_update_get_next_change to iterate over the collected tiles.
|
||||
*
|
||||
* \returns ePartialUpdateCollectResult::FullUpdateNeeded: called should not use partial updates
|
||||
* but recalculate the full image. This result can be expected when called for the first time for a
|
||||
* user and when it isn't possible to reconstruct the changes as the internal state doesn't have
|
||||
* enough data stored. ePartialUpdateCollectResult::NoChangesDetected: The have been no changes
|
||||
* detected since last invoke for the same user.
|
||||
* ePartialUpdateCollectResult::PartialChangesDetected: Parts of the image has been updated since
|
||||
* last invoke for the same user. The changes can be read by using
|
||||
* #BKE_image_partial_update_get_next_change.
|
||||
*/
|
||||
ePartialUpdateCollectResult BKE_image_partial_update_collect_changes(
|
||||
struct Image *image, struct PartialUpdateUser *user);
|
||||
|
||||
ePartialUpdateIterResult BKE_image_partial_update_get_next_change(
|
||||
struct PartialUpdateUser *user, struct PartialUpdateRegion *r_region);
|
||||
|
||||
/** \brief Abstract class to load tile data when using the PartialUpdateChecker. */
|
||||
class AbstractTileData {
|
||||
protected:
|
||||
virtual ~AbstractTileData() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief Load the data for the given tile_number.
|
||||
*
|
||||
* Invoked when changes are on a different tile compared to the previous tile..
|
||||
*/
|
||||
virtual void init_data(TileNumber tile_number) = 0;
|
||||
/**
|
||||
* \brief Unload the data that has been loaded.
|
||||
*
|
||||
* Invoked when changes are on a different tile compared to the previous tile or when finished
|
||||
* iterating over the changes.
|
||||
*/
|
||||
virtual void free_data() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Class to not load any tile specific data when iterating over changes.
|
||||
*/
|
||||
class NoTileData : AbstractTileData {
|
||||
public:
|
||||
NoTileData(Image *UNUSED(image), ImageUser *UNUSED(image_user))
|
||||
{
|
||||
}
|
||||
|
||||
void init_data(TileNumber UNUSED(new_tile_number)) override
|
||||
{
|
||||
}
|
||||
|
||||
void free_data() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Load the ImageTile and ImBuf associated with the partial change.
|
||||
*/
|
||||
class ImageTileData : AbstractTileData {
|
||||
public:
|
||||
/**
|
||||
* \brief Not owned Image that is being iterated over.
|
||||
*/
|
||||
Image *image;
|
||||
|
||||
/**
|
||||
* \brief Local copy of the image user.
|
||||
*
|
||||
* The local copy is required so we don't change the image user of the caller.
|
||||
* We need to change it in order to request data for a specific tile.
|
||||
*/
|
||||
ImageUser image_user = {0};
|
||||
|
||||
/**
|
||||
* \brief ImageTile associated with the loaded tile.
|
||||
* Data is not owned by this instance but by the `image`.
|
||||
*/
|
||||
ImageTile *tile = nullptr;
|
||||
|
||||
/**
|
||||
* \brief ImBuf of the loaded tile.
|
||||
*
|
||||
* Can be nullptr when the file doesn't exist or when the tile hasn't been initialized.
|
||||
*/
|
||||
ImBuf *tile_buffer = nullptr;
|
||||
|
||||
ImageTileData(Image *image, ImageUser *image_user) : image(image)
|
||||
{
|
||||
if (image_user != nullptr) {
|
||||
this->image_user = *image_user;
|
||||
}
|
||||
}
|
||||
|
||||
void init_data(TileNumber new_tile_number) override
|
||||
{
|
||||
image_user.tile = new_tile_number;
|
||||
tile = BKE_image_get_tile(image, new_tile_number);
|
||||
tile_buffer = BKE_image_acquire_ibuf(image, &image_user, NULL);
|
||||
}
|
||||
|
||||
void free_data() override
|
||||
{
|
||||
BKE_image_release_ibuf(image, tile_buffer, nullptr);
|
||||
tile = nullptr;
|
||||
tile_buffer = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TileData = NoTileData> struct PartialUpdateChecker {
|
||||
|
||||
/**
|
||||
* \brief Not owned Image that is being iterated over.
|
||||
*/
|
||||
Image *image;
|
||||
ImageUser *image_user;
|
||||
|
||||
/**
|
||||
* \brief the collected changes are stored inside the PartialUpdateUser.
|
||||
*/
|
||||
PartialUpdateUser *user;
|
||||
|
||||
struct CollectResult {
|
||||
PartialUpdateChecker<TileData> *checker;
|
||||
|
||||
/**
|
||||
* \brief Tile specific data.
|
||||
*/
|
||||
TileData tile_data;
|
||||
PartialUpdateRegion changed_region;
|
||||
ePartialUpdateCollectResult result_code;
|
||||
|
||||
private:
|
||||
TileNumber last_tile_number;
|
||||
|
||||
public:
|
||||
CollectResult(PartialUpdateChecker<TileData> *checker, ePartialUpdateCollectResult result_code)
|
||||
: checker(checker),
|
||||
tile_data(checker->image, checker->image_user),
|
||||
result_code(result_code)
|
||||
{
|
||||
}
|
||||
|
||||
const ePartialUpdateCollectResult get_result_code() const
|
||||
{
|
||||
return result_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Load the next changed region.
|
||||
*
|
||||
* This member function can only be called when partial changes are detected.
|
||||
* (`get_result_code()` returns `ePartialUpdateCollectResult::PartialChangesDetected`).
|
||||
*
|
||||
* When changes for another tile than the previous tile is loaded the #tile_data will be
|
||||
* updated.
|
||||
*/
|
||||
ePartialUpdateIterResult get_next_change()
|
||||
{
|
||||
BLI_assert(result_code == ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
ePartialUpdateIterResult result = BKE_image_partial_update_get_next_change(checker->user,
|
||||
&changed_region);
|
||||
switch (result) {
|
||||
case ePartialUpdateIterResult::Finished:
|
||||
tile_data.free_data();
|
||||
return result;
|
||||
|
||||
case ePartialUpdateIterResult::ChangeAvailable:
|
||||
if (last_tile_number == changed_region.tile_number) {
|
||||
return result;
|
||||
}
|
||||
tile_data.free_data();
|
||||
tile_data.init_data(changed_region.tile_number);
|
||||
last_tile_number = changed_region.tile_number;
|
||||
return result;
|
||||
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
PartialUpdateChecker(Image *image, ImageUser *image_user, PartialUpdateUser *user)
|
||||
: image(image), image_user(image_user), user(user)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check for new changes since the last time this method was invoked for this #user.
|
||||
*/
|
||||
CollectResult collect_changes()
|
||||
{
|
||||
ePartialUpdateCollectResult collect_result = BKE_image_partial_update_collect_changes(image,
|
||||
user);
|
||||
return CollectResult(this, collect_result);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace partial_update
|
||||
} // namespace blender::bke::image
|
@@ -165,7 +165,6 @@ set(SRC
|
||||
intern/idprop_utils.c
|
||||
intern/idtype.c
|
||||
intern/image.c
|
||||
intern/image_partial_update.cc
|
||||
intern/image_gen.c
|
||||
intern/image_gpu.cc
|
||||
intern/image_save.c
|
||||
@@ -823,7 +822,6 @@ if(WITH_GTESTS)
|
||||
intern/cryptomatte_test.cc
|
||||
intern/fcurve_test.cc
|
||||
intern/idprop_serialize_test.cc
|
||||
intern/image_partial_update_test.cc
|
||||
intern/lattice_deform_test.cc
|
||||
intern/layer_test.cc
|
||||
intern/lib_id_remapper_test.cc
|
||||
|
@@ -218,7 +218,7 @@ void *BKE_camera_add(Main *bmain, const char *name)
|
||||
return cam;
|
||||
}
|
||||
|
||||
float BKE_camera_object_dof_distance(const Object *ob)
|
||||
float BKE_camera_object_dof_distance(Object *ob)
|
||||
{
|
||||
Camera *cam = (Camera *)ob->data;
|
||||
if (ob->type != OB_CAMERA) {
|
||||
|
@@ -66,8 +66,8 @@ static void vert_extrude_to_mesh_data(const Spline &spline,
|
||||
|
||||
if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
|
||||
MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1];
|
||||
edge.v1 = vert_offset + eval_size - 1;
|
||||
edge.v2 = vert_offset;
|
||||
edge.v1 = vert_offset;
|
||||
edge.v2 = vert_offset + eval_size - 1;
|
||||
edge.flag = ME_LOOSEEDGE;
|
||||
}
|
||||
|
||||
|
@@ -2427,13 +2427,6 @@ int CustomData_get_stencil_layer(const CustomData *data, int type)
|
||||
return (layer_index != -1) ? data->layers[layer_index].active_mask : -1;
|
||||
}
|
||||
|
||||
const char *CustomData_get_active_layer_name(const struct CustomData *data, const int type)
|
||||
{
|
||||
/* Get the layer index of the active layer of this type. */
|
||||
const int layer_index = CustomData_get_active_layer_index(data, type);
|
||||
return layer_index < 0 ? NULL : data->layers[layer_index].name;
|
||||
}
|
||||
|
||||
void CustomData_set_layer_active(CustomData *data, int type, int n)
|
||||
{
|
||||
for (int i = 0; i < data->totlayer; i++) {
|
||||
|
@@ -134,22 +134,6 @@ static void image_runtime_reset_on_copy(struct Image *image)
|
||||
{
|
||||
image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex");
|
||||
BLI_mutex_init(image->runtime.cache_mutex);
|
||||
|
||||
image->runtime.partial_update_register = NULL;
|
||||
image->runtime.partial_update_user = NULL;
|
||||
}
|
||||
|
||||
static void image_runtime_free_data(struct Image *image)
|
||||
{
|
||||
BLI_mutex_end(image->runtime.cache_mutex);
|
||||
MEM_freeN(image->runtime.cache_mutex);
|
||||
image->runtime.cache_mutex = NULL;
|
||||
|
||||
if (image->runtime.partial_update_user != NULL) {
|
||||
BKE_image_partial_update_free(image->runtime.partial_update_user);
|
||||
image->runtime.partial_update_user = NULL;
|
||||
}
|
||||
BKE_image_partial_update_register_free(image);
|
||||
}
|
||||
|
||||
static void image_init_data(ID *id)
|
||||
@@ -229,8 +213,10 @@ static void image_free_data(ID *id)
|
||||
BKE_previewimg_free(&image->preview);
|
||||
|
||||
BLI_freelistN(&image->tiles);
|
||||
BLI_freelistN(&image->gpu_refresh_areas);
|
||||
|
||||
image_runtime_free_data(image);
|
||||
BLI_mutex_end(image->runtime.cache_mutex);
|
||||
MEM_freeN(image->runtime.cache_mutex);
|
||||
}
|
||||
|
||||
static void image_foreach_cache(ID *id,
|
||||
@@ -335,8 +321,7 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
||||
ima->cache = NULL;
|
||||
ima->gpuflag = 0;
|
||||
BLI_listbase_clear(&ima->anims);
|
||||
ima->runtime.partial_update_register = NULL;
|
||||
ima->runtime.partial_update_user = NULL;
|
||||
BLI_listbase_clear(&ima->gpu_refresh_areas);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) {
|
||||
@@ -416,6 +401,7 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
|
||||
ima->lastused = 0;
|
||||
ima->gpuflag = 0;
|
||||
BLI_listbase_clear(&ima->gpu_refresh_areas);
|
||||
|
||||
image_runtime_reset(ima);
|
||||
}
|
||||
|
@@ -38,7 +38,6 @@
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_partial_update.hh"
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
@@ -47,10 +46,6 @@
|
||||
|
||||
#include "PIL_time.h"
|
||||
|
||||
using namespace blender::bke::image::partial_update;
|
||||
|
||||
extern "C" {
|
||||
|
||||
/* Prototypes. */
|
||||
static void gpu_free_unused_buffers();
|
||||
static void image_free_gpu(Image *ima, const bool immediate);
|
||||
@@ -58,6 +53,14 @@ static void image_free_gpu_limited_scale(Image *ima);
|
||||
static void image_update_gputexture_ex(
|
||||
Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h);
|
||||
|
||||
/* Internal structs. */
|
||||
#define IMA_PARTIAL_REFRESH_TILE_SIZE 256
|
||||
struct ImagePartialRefresh {
|
||||
struct ImagePartialRefresh *next, *prev;
|
||||
int tile_x;
|
||||
int tile_y;
|
||||
};
|
||||
|
||||
bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf)
|
||||
{
|
||||
if (image) {
|
||||
@@ -334,48 +337,6 @@ static void image_update_reusable_textures(Image *ima,
|
||||
}
|
||||
}
|
||||
|
||||
static void image_gpu_texture_partial_update_changes_available(
|
||||
Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes)
|
||||
{
|
||||
while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
|
||||
const int tile_offset_x = changes.changed_region.region.xmin;
|
||||
const int tile_offset_y = changes.changed_region.region.ymin;
|
||||
const int tile_width = min_ii(changes.tile_data.tile_buffer->x,
|
||||
BLI_rcti_size_x(&changes.changed_region.region));
|
||||
const int tile_height = min_ii(changes.tile_data.tile_buffer->y,
|
||||
BLI_rcti_size_y(&changes.changed_region.region));
|
||||
image_update_gputexture_ex(image,
|
||||
changes.tile_data.tile,
|
||||
changes.tile_data.tile_buffer,
|
||||
tile_offset_x,
|
||||
tile_offset_y,
|
||||
tile_width,
|
||||
tile_height);
|
||||
}
|
||||
}
|
||||
|
||||
static void image_gpu_texture_try_partial_update(Image *image, ImageUser *iuser)
|
||||
{
|
||||
PartialUpdateChecker<ImageTileData> checker(image, iuser, image->runtime.partial_update_user);
|
||||
PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes();
|
||||
switch (changes.get_result_code()) {
|
||||
case ePartialUpdateCollectResult::FullUpdateNeeded: {
|
||||
image_free_gpu(image, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case ePartialUpdateCollectResult::PartialChangesDetected: {
|
||||
image_gpu_texture_partial_update_changes_available(image, changes);
|
||||
break;
|
||||
}
|
||||
|
||||
case ePartialUpdateCollectResult::NoChangesDetected: {
|
||||
/* GPUTextures are up to date. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GPUTexture *image_get_gpu_texture(Image *ima,
|
||||
ImageUser *iuser,
|
||||
ImBuf *ibuf,
|
||||
@@ -409,20 +370,31 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
|
||||
}
|
||||
#undef GPU_FLAGS_TO_CHECK
|
||||
|
||||
/* TODO(jbakker): We should replace the IMA_GPU_REFRESH flag with a call to
|
||||
* BKE_image-partial_update_mark_full_update. Although the flag is quicker it leads to double
|
||||
* administration. */
|
||||
if ((ima->gpuflag & IMA_GPU_REFRESH) != 0) {
|
||||
BKE_image_partial_update_mark_full_update(ima);
|
||||
ima->gpuflag &= ~IMA_GPU_REFRESH;
|
||||
/* Check if image has been updated and tagged to be updated (full or partial). */
|
||||
ImageTile *tile = BKE_image_get_tile(ima, 0);
|
||||
if (((ima->gpuflag & IMA_GPU_REFRESH) != 0) ||
|
||||
((ibuf == nullptr || tile == nullptr) && ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) {
|
||||
image_free_gpu(ima, true);
|
||||
BLI_freelistN(&ima->gpu_refresh_areas);
|
||||
ima->gpuflag &= ~(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH);
|
||||
}
|
||||
|
||||
if (ima->runtime.partial_update_user == nullptr) {
|
||||
ima->runtime.partial_update_user = BKE_image_partial_update_create(ima);
|
||||
else if (ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) {
|
||||
BLI_assert(ibuf);
|
||||
BLI_assert(tile);
|
||||
ImagePartialRefresh *refresh_area;
|
||||
while ((
|
||||
refresh_area = static_cast<ImagePartialRefresh *>(BLI_pophead(&ima->gpu_refresh_areas)))) {
|
||||
const int tile_offset_x = refresh_area->tile_x * IMA_PARTIAL_REFRESH_TILE_SIZE;
|
||||
const int tile_offset_y = refresh_area->tile_y * IMA_PARTIAL_REFRESH_TILE_SIZE;
|
||||
const int tile_width = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->x - tile_offset_x);
|
||||
const int tile_height = MIN2(IMA_PARTIAL_REFRESH_TILE_SIZE, ibuf->y - tile_offset_y);
|
||||
image_update_gputexture_ex(
|
||||
ima, tile, ibuf, tile_offset_x, tile_offset_y, tile_width, tile_height);
|
||||
MEM_freeN(refresh_area);
|
||||
}
|
||||
ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH;
|
||||
}
|
||||
|
||||
image_gpu_texture_try_partial_update(ima, iuser);
|
||||
|
||||
/* Tag as in active use for garbage collector. */
|
||||
BKE_image_tag_time(ima);
|
||||
|
||||
@@ -445,7 +417,6 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
|
||||
|
||||
/* Check if we have a valid image. If not, we return a dummy
|
||||
* texture with zero bind-code so we don't keep trying. */
|
||||
ImageTile *tile = BKE_image_get_tile(ima, 0);
|
||||
if (tile == nullptr) {
|
||||
*tex = image_gpu_texture_error_create(textarget);
|
||||
return *tex;
|
||||
@@ -456,7 +427,8 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
|
||||
if (ibuf_intern == nullptr) {
|
||||
ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, nullptr);
|
||||
if (ibuf_intern == nullptr) {
|
||||
return image_gpu_texture_error_create(textarget);
|
||||
*tex = image_gpu_texture_error_create(textarget);
|
||||
return *tex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,12 +477,13 @@ static GPUTexture *image_get_gpu_texture(Image *ima,
|
||||
break;
|
||||
}
|
||||
|
||||
if (*tex) {
|
||||
GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
|
||||
/* if `ibuf` was given, we don't own the `ibuf_intern` */
|
||||
if (ibuf == nullptr) {
|
||||
BKE_image_release_ibuf(ima, ibuf_intern, nullptr);
|
||||
}
|
||||
|
||||
if (ibuf != ibuf_intern) {
|
||||
BKE_image_release_ibuf(ima, ibuf_intern, nullptr);
|
||||
if (*tex) {
|
||||
GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y);
|
||||
}
|
||||
|
||||
return *tex;
|
||||
@@ -930,33 +903,87 @@ static void image_update_gputexture_ex(
|
||||
|
||||
void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
|
||||
{
|
||||
ImageTile *image_tile = BKE_image_get_tile_from_iuser(ima, iuser);
|
||||
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, nullptr);
|
||||
BKE_image_update_gputexture_delayed(ima, image_tile, ibuf, x, y, w, h);
|
||||
ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser);
|
||||
|
||||
if ((ibuf == nullptr) || (w == 0) || (h == 0)) {
|
||||
/* Full reload of texture. */
|
||||
BKE_image_free_gputextures(ima);
|
||||
}
|
||||
image_update_gputexture_ex(ima, tile, ibuf, x, y, w, h);
|
||||
BKE_image_release_ibuf(ima, ibuf, nullptr);
|
||||
}
|
||||
|
||||
/* Mark areas on the GPUTexture that needs to be updated. The areas are marked in chunks.
|
||||
* The next time the GPUTexture is used these tiles will be refreshes. This saves time
|
||||
* when writing to the same place multiple times This happens for during foreground
|
||||
* rendering. */
|
||||
void BKE_image_update_gputexture_delayed(struct Image *ima,
|
||||
struct ImageTile *image_tile,
|
||||
struct ImBuf *ibuf,
|
||||
int x,
|
||||
int y,
|
||||
int w,
|
||||
int h)
|
||||
void BKE_image_update_gputexture_delayed(
|
||||
struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h)
|
||||
{
|
||||
/* Check for full refresh. */
|
||||
if (ibuf != nullptr && ima->source != IMA_SRC_TILED && x == 0 && y == 0 && w == ibuf->x &&
|
||||
h == ibuf->y) {
|
||||
BKE_image_partial_update_mark_full_update(ima);
|
||||
if (ibuf && x == 0 && y == 0 && w == ibuf->x && h == ibuf->y) {
|
||||
ima->gpuflag |= IMA_GPU_REFRESH;
|
||||
}
|
||||
/* Check if we can promote partial refresh to a full refresh. */
|
||||
if ((ima->gpuflag & (IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) ==
|
||||
(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH)) {
|
||||
ima->gpuflag &= ~IMA_GPU_PARTIAL_REFRESH;
|
||||
BLI_freelistN(&ima->gpu_refresh_areas);
|
||||
}
|
||||
/* Image is already marked for complete refresh. */
|
||||
if (ima->gpuflag & IMA_GPU_REFRESH) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Schedule the tiles that covers the requested area. */
|
||||
const int start_tile_x = x / IMA_PARTIAL_REFRESH_TILE_SIZE;
|
||||
const int start_tile_y = y / IMA_PARTIAL_REFRESH_TILE_SIZE;
|
||||
const int end_tile_x = (x + w) / IMA_PARTIAL_REFRESH_TILE_SIZE;
|
||||
const int end_tile_y = (y + h) / IMA_PARTIAL_REFRESH_TILE_SIZE;
|
||||
const int num_tiles_x = (end_tile_x + 1) - (start_tile_x);
|
||||
const int num_tiles_y = (end_tile_y + 1) - (start_tile_y);
|
||||
const int num_tiles = num_tiles_x * num_tiles_y;
|
||||
const bool allocate_on_heap = BLI_BITMAP_SIZE(num_tiles) > 16;
|
||||
BLI_bitmap *requested_tiles = nullptr;
|
||||
if (allocate_on_heap) {
|
||||
requested_tiles = BLI_BITMAP_NEW(num_tiles, __func__);
|
||||
}
|
||||
else {
|
||||
rcti dirty_region;
|
||||
BLI_rcti_init(&dirty_region, x, x + w, y, y + h);
|
||||
BKE_image_partial_update_mark_region(ima, image_tile, ibuf, &dirty_region);
|
||||
requested_tiles = BLI_BITMAP_NEW_ALLOCA(num_tiles);
|
||||
}
|
||||
|
||||
/* Mark the tiles that have already been requested. They don't need to be requested again. */
|
||||
int num_tiles_not_scheduled = num_tiles;
|
||||
LISTBASE_FOREACH (ImagePartialRefresh *, area, &ima->gpu_refresh_areas) {
|
||||
if (area->tile_x < start_tile_x || area->tile_x > end_tile_x || area->tile_y < start_tile_y ||
|
||||
area->tile_y > end_tile_y) {
|
||||
continue;
|
||||
}
|
||||
int requested_tile_index = (area->tile_x - start_tile_x) +
|
||||
(area->tile_y - start_tile_y) * num_tiles_x;
|
||||
BLI_BITMAP_ENABLE(requested_tiles, requested_tile_index);
|
||||
num_tiles_not_scheduled--;
|
||||
if (num_tiles_not_scheduled == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule the tiles that aren't requested yet. */
|
||||
if (num_tiles_not_scheduled) {
|
||||
int tile_index = 0;
|
||||
for (int tile_y = start_tile_y; tile_y <= end_tile_y; tile_y++) {
|
||||
for (int tile_x = start_tile_x; tile_x <= end_tile_x; tile_x++) {
|
||||
if (!BLI_BITMAP_TEST_BOOL(requested_tiles, tile_index)) {
|
||||
ImagePartialRefresh *area = static_cast<ImagePartialRefresh *>(
|
||||
MEM_mallocN(sizeof(ImagePartialRefresh), __func__));
|
||||
area->tile_x = tile_x;
|
||||
area->tile_y = tile_y;
|
||||
BLI_addtail(&ima->gpu_refresh_areas, area);
|
||||
}
|
||||
tile_index++;
|
||||
}
|
||||
}
|
||||
ima->gpuflag |= IMA_GPU_PARTIAL_REFRESH;
|
||||
}
|
||||
if (allocate_on_heap) {
|
||||
MEM_freeN(requested_tiles);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -989,4 +1016,3 @@ void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap)
|
||||
}
|
||||
|
||||
/** \} */
|
||||
}
|
||||
|
@@ -1,598 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
/**
|
||||
* \file image_gpu_partial_update.cc
|
||||
* \ingroup bke
|
||||
*
|
||||
* To reduce the overhead of image processing this file contains a mechanism to detect areas of the
|
||||
* image that are changed. These areas are organized in chunks. Changes that happen over time are
|
||||
* organized in changesets.
|
||||
*
|
||||
* A common usecase is to update GPUTexture for drawing where only that part is uploaded that only
|
||||
* changed.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```
|
||||
* Image *image = ...;
|
||||
* ImBuf *image_buffer = ...;
|
||||
*
|
||||
* // Partial_update_user should be kept for the whole session where the changes needs to be
|
||||
* // tracked. Keep this instance alive as long as you need to track image changes.
|
||||
*
|
||||
* PartialUpdateUser *partial_update_user = BKE_image_partial_update_create(image);
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* switch (BKE_image_partial_update_collect_changes(image, image_buffer))
|
||||
* {
|
||||
* case ePartialUpdateCollectResult::FullUpdateNeeded:
|
||||
* // Unable to do partial updates. Perform a full update.
|
||||
* break;
|
||||
* case ePartialUpdateCollectResult::PartialChangesDetected:
|
||||
* PartialUpdateRegion change;
|
||||
* while (BKE_image_partial_update_get_next_change(partial_update_user, &change) ==
|
||||
* ePartialUpdateIterResult::ChangeAvailable){
|
||||
* // Do something with the change.
|
||||
* }
|
||||
* case ePartialUpdateCollectResult::NoChangesDetected:
|
||||
* break;
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* // Free partial_update_user.
|
||||
* BKE_image_partial_update_free(partial_update_user);
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_partial_update.hh"
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender::bke::image::partial_update {
|
||||
|
||||
/** \brief Size of chunks to track changes. */
|
||||
constexpr int CHUNK_SIZE = 256;
|
||||
|
||||
/**
|
||||
* \brief Max number of changesets to keep in history.
|
||||
*
|
||||
* A higher number would need more memory and processing
|
||||
* to calculate a changeset, but would lead to do partial updates for requests that don't happen
|
||||
* every frame.
|
||||
*
|
||||
* A to small number would lead to more full updates when changes couldn't be reconstructed from
|
||||
* the available history.
|
||||
*/
|
||||
constexpr int MAX_HISTORY_LEN = 4;
|
||||
|
||||
/**
|
||||
* \brief get the chunk number for the give pixel coordinate.
|
||||
*
|
||||
* As chunks are squares the this member can be used for both x and y axis.
|
||||
*/
|
||||
static int chunk_number_for_pixel(int pixel_offset)
|
||||
{
|
||||
int chunk_offset = pixel_offset / CHUNK_SIZE;
|
||||
if (pixel_offset < 0) {
|
||||
chunk_offset -= 1;
|
||||
}
|
||||
return chunk_offset;
|
||||
}
|
||||
|
||||
struct PartialUpdateUserImpl;
|
||||
struct PartialUpdateRegisterImpl;
|
||||
|
||||
/**
|
||||
* Wrap PartialUpdateUserImpl to its C-struct (PartialUpdateUser).
|
||||
*/
|
||||
static struct PartialUpdateUser *wrap(PartialUpdateUserImpl *user)
|
||||
{
|
||||
return static_cast<struct PartialUpdateUser *>(static_cast<void *>(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap the PartialUpdateUser C-struct to its CPP counterpart (PartialUpdateUserImpl).
|
||||
*/
|
||||
static PartialUpdateUserImpl *unwrap(struct PartialUpdateUser *user)
|
||||
{
|
||||
return static_cast<PartialUpdateUserImpl *>(static_cast<void *>(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap PartialUpdateRegisterImpl to its C-struct (PartialUpdateRegister).
|
||||
*/
|
||||
static struct PartialUpdateRegister *wrap(PartialUpdateRegisterImpl *partial_update_register)
|
||||
{
|
||||
return static_cast<struct PartialUpdateRegister *>(static_cast<void *>(partial_update_register));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap the PartialUpdateRegister C-struct to its CPP counterpart (PartialUpdateRegisterImpl).
|
||||
*/
|
||||
static PartialUpdateRegisterImpl *unwrap(struct PartialUpdateRegister *partial_update_register)
|
||||
{
|
||||
return static_cast<PartialUpdateRegisterImpl *>(static_cast<void *>(partial_update_register));
|
||||
}
|
||||
|
||||
using TileNumber = int32_t;
|
||||
using ChangesetID = int64_t;
|
||||
constexpr ChangesetID UnknownChangesetID = -1;
|
||||
|
||||
struct PartialUpdateUserImpl {
|
||||
/** \brief last changeset id that was seen by this user. */
|
||||
ChangesetID last_changeset_id = UnknownChangesetID;
|
||||
|
||||
/** \brief regions that have been updated. */
|
||||
Vector<PartialUpdateRegion> updated_regions;
|
||||
|
||||
#ifdef NDEBUG
|
||||
/** \brief reference to image to validate correct API usage. */
|
||||
const void *debug_image_;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Clear the list of updated regions.
|
||||
*
|
||||
* Updated regions should be cleared at the start of #BKE_image_partial_update_collect_changes so
|
||||
* the
|
||||
*/
|
||||
void clear_updated_regions()
|
||||
{
|
||||
updated_regions.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Dirty chunks of an ImageTile.
|
||||
*
|
||||
* Internally dirty tiles are grouped together in change sets to make sure that the correct
|
||||
* answer can be built for different users reducing the amount of merges.
|
||||
*/
|
||||
struct TileChangeset {
|
||||
private:
|
||||
/** \brief Dirty flag for each chunk. */
|
||||
std::vector<bool> chunk_dirty_flags_;
|
||||
/** \brief are there dirty/ */
|
||||
bool has_dirty_chunks_ = false;
|
||||
|
||||
public:
|
||||
/** \brief Width of the tile in pixels. */
|
||||
int tile_width;
|
||||
/** \brief Height of the tile in pixels. */
|
||||
int tile_height;
|
||||
/** \brief Number of chunks along the x-axis. */
|
||||
int chunk_x_len;
|
||||
/** \brief Number of chunks along the y-axis. */
|
||||
int chunk_y_len;
|
||||
|
||||
TileNumber tile_number;
|
||||
|
||||
void clear()
|
||||
{
|
||||
init_chunks(chunk_x_len, chunk_y_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Update the resolution of the tile.
|
||||
*
|
||||
* \returns true: resolution has been updated.
|
||||
* false: resolution was unchanged.
|
||||
*/
|
||||
bool update_resolution(const ImBuf *image_buffer)
|
||||
{
|
||||
if (tile_width == image_buffer->x && tile_height == image_buffer->y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tile_width = image_buffer->x;
|
||||
tile_height = image_buffer->y;
|
||||
|
||||
int chunk_x_len = tile_width / CHUNK_SIZE;
|
||||
int chunk_y_len = tile_height / CHUNK_SIZE;
|
||||
init_chunks(chunk_x_len, chunk_y_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mark_region(const rcti *updated_region)
|
||||
{
|
||||
int start_x_chunk = chunk_number_for_pixel(updated_region->xmin);
|
||||
int end_x_chunk = chunk_number_for_pixel(updated_region->xmax - 1);
|
||||
int start_y_chunk = chunk_number_for_pixel(updated_region->ymin);
|
||||
int end_y_chunk = chunk_number_for_pixel(updated_region->ymax - 1);
|
||||
|
||||
/* Clamp tiles to tiles in image. */
|
||||
start_x_chunk = max_ii(0, start_x_chunk);
|
||||
start_y_chunk = max_ii(0, start_y_chunk);
|
||||
end_x_chunk = min_ii(chunk_x_len - 1, end_x_chunk);
|
||||
end_y_chunk = min_ii(chunk_y_len - 1, end_y_chunk);
|
||||
|
||||
/* Early exit when no tiles need to be updated. */
|
||||
if (start_x_chunk >= chunk_x_len) {
|
||||
return;
|
||||
}
|
||||
if (start_y_chunk >= chunk_y_len) {
|
||||
return;
|
||||
}
|
||||
if (end_x_chunk < 0) {
|
||||
return;
|
||||
}
|
||||
if (end_y_chunk < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mark_chunks_dirty(start_x_chunk, start_y_chunk, end_x_chunk, end_y_chunk);
|
||||
}
|
||||
|
||||
void mark_chunks_dirty(int start_x_chunk, int start_y_chunk, int end_x_chunk, int end_y_chunk)
|
||||
{
|
||||
for (int chunk_y = start_y_chunk; chunk_y <= end_y_chunk; chunk_y++) {
|
||||
for (int chunk_x = start_x_chunk; chunk_x <= end_x_chunk; chunk_x++) {
|
||||
int chunk_index = chunk_y * chunk_x_len + chunk_x;
|
||||
chunk_dirty_flags_[chunk_index] = true;
|
||||
}
|
||||
}
|
||||
has_dirty_chunks_ = true;
|
||||
}
|
||||
|
||||
bool has_dirty_chunks() const
|
||||
{
|
||||
return has_dirty_chunks_;
|
||||
}
|
||||
|
||||
void init_chunks(int chunk_x_len_, int chunk_y_len_)
|
||||
{
|
||||
chunk_x_len = chunk_x_len_;
|
||||
chunk_y_len = chunk_y_len_;
|
||||
const int chunk_len = chunk_x_len * chunk_y_len;
|
||||
const int previous_chunk_len = chunk_dirty_flags_.size();
|
||||
|
||||
chunk_dirty_flags_.resize(chunk_len);
|
||||
/* Fast exit. When the changeset was already empty no need to re-init the chunk_validity. */
|
||||
if (!has_dirty_chunks()) {
|
||||
return;
|
||||
}
|
||||
for (int index = 0; index < min_ii(chunk_len, previous_chunk_len); index++) {
|
||||
chunk_dirty_flags_[index] = false;
|
||||
}
|
||||
has_dirty_chunks_ = false;
|
||||
}
|
||||
|
||||
/** \brief Merge the given changeset into the receiver. */
|
||||
void merge(const TileChangeset &other)
|
||||
{
|
||||
BLI_assert(chunk_x_len == other.chunk_x_len);
|
||||
BLI_assert(chunk_y_len == other.chunk_y_len);
|
||||
const int chunk_len = chunk_x_len * chunk_y_len;
|
||||
|
||||
for (int chunk_index = 0; chunk_index < chunk_len; chunk_index++) {
|
||||
chunk_dirty_flags_[chunk_index] = chunk_dirty_flags_[chunk_index] |
|
||||
other.chunk_dirty_flags_[chunk_index];
|
||||
}
|
||||
has_dirty_chunks_ |= other.has_dirty_chunks_;
|
||||
}
|
||||
|
||||
/** \brief has a chunk changed inside this changeset. */
|
||||
bool is_chunk_dirty(int chunk_x, int chunk_y) const
|
||||
{
|
||||
const int chunk_index = chunk_y * chunk_x_len + chunk_x;
|
||||
return chunk_dirty_flags_[chunk_index];
|
||||
}
|
||||
};
|
||||
|
||||
/** \brief Changeset keeping track of changes for an image */
|
||||
struct Changeset {
|
||||
private:
|
||||
Vector<TileChangeset> tiles;
|
||||
|
||||
public:
|
||||
/** \brief Keep track if any of the tiles have dirty chunks. */
|
||||
bool has_dirty_chunks;
|
||||
|
||||
/**
|
||||
* \brief Retrieve the TileChangeset for the given ImageTile.
|
||||
*
|
||||
* When the TileChangeset isn't found, it will be added.
|
||||
*/
|
||||
TileChangeset &operator[](const ImageTile *image_tile)
|
||||
{
|
||||
for (TileChangeset &tile_changeset : tiles) {
|
||||
if (tile_changeset.tile_number == image_tile->tile_number) {
|
||||
return tile_changeset;
|
||||
}
|
||||
}
|
||||
|
||||
TileChangeset tile_changeset;
|
||||
tile_changeset.tile_number = image_tile->tile_number;
|
||||
tiles.append_as(tile_changeset);
|
||||
|
||||
return tiles.last();
|
||||
}
|
||||
|
||||
/** \brief Does this changeset contain data for the given tile. */
|
||||
bool has_tile(const ImageTile *image_tile)
|
||||
{
|
||||
for (TileChangeset &tile_changeset : tiles) {
|
||||
if (tile_changeset.tile_number == image_tile->tile_number) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \brief Clear this changeset. */
|
||||
void clear()
|
||||
{
|
||||
tiles.clear();
|
||||
has_dirty_chunks = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Partial update changes stored inside the image runtime.
|
||||
*
|
||||
* The PartialUpdateRegisterImpl will keep track of changes over time. Changes are groups inside
|
||||
* TileChangesets.
|
||||
*/
|
||||
struct PartialUpdateRegisterImpl {
|
||||
/** \brief changeset id of the first changeset kept in #history. */
|
||||
ChangesetID first_changeset_id;
|
||||
/** \brief changeset id of the top changeset kept in #history. */
|
||||
ChangesetID last_changeset_id;
|
||||
|
||||
/** \brief history of changesets. */
|
||||
Vector<Changeset> history;
|
||||
/** \brief The current changeset. New changes will be added to this changeset. */
|
||||
Changeset current_changeset;
|
||||
|
||||
void update_resolution(const ImageTile *image_tile, const ImBuf *image_buffer)
|
||||
{
|
||||
TileChangeset &tile_changeset = current_changeset[image_tile];
|
||||
const bool has_dirty_chunks = tile_changeset.has_dirty_chunks();
|
||||
const bool resolution_changed = tile_changeset.update_resolution(image_buffer);
|
||||
|
||||
if (has_dirty_chunks && resolution_changed && !history.is_empty()) {
|
||||
mark_full_update();
|
||||
}
|
||||
}
|
||||
|
||||
void mark_full_update()
|
||||
{
|
||||
history.clear();
|
||||
last_changeset_id++;
|
||||
current_changeset.clear();
|
||||
first_changeset_id = last_changeset_id;
|
||||
}
|
||||
|
||||
void mark_region(const ImageTile *image_tile, const rcti *updated_region)
|
||||
{
|
||||
TileChangeset &tile_changeset = current_changeset[image_tile];
|
||||
tile_changeset.mark_region(updated_region);
|
||||
current_changeset.has_dirty_chunks |= tile_changeset.has_dirty_chunks();
|
||||
}
|
||||
|
||||
void ensure_empty_changeset()
|
||||
{
|
||||
if (!current_changeset.has_dirty_chunks) {
|
||||
/* No need to create a new changeset when previous changeset does not contain any dirty
|
||||
* tiles. */
|
||||
return;
|
||||
}
|
||||
commit_current_changeset();
|
||||
limit_history();
|
||||
}
|
||||
|
||||
/** \brief Move the current changeset to the history and resets the current changeset. */
|
||||
void commit_current_changeset()
|
||||
{
|
||||
history.append_as(std::move(current_changeset));
|
||||
current_changeset.clear();
|
||||
last_changeset_id++;
|
||||
}
|
||||
|
||||
/** \brief Limit the number of items in the changeset. */
|
||||
void limit_history()
|
||||
{
|
||||
const int num_items_to_remove = max_ii(history.size() - MAX_HISTORY_LEN, 0);
|
||||
if (num_items_to_remove == 0) {
|
||||
return;
|
||||
}
|
||||
history.remove(0, num_items_to_remove);
|
||||
first_changeset_id += num_items_to_remove;
|
||||
}
|
||||
|
||||
/**
|
||||
* /brief Check if data is available to construct the update tiles for the given
|
||||
* changeset_id.
|
||||
*
|
||||
* The update tiles can be created when changeset id is between
|
||||
*/
|
||||
bool can_construct(ChangesetID changeset_id)
|
||||
{
|
||||
return changeset_id >= first_changeset_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief collect all historic changes since a given changeset.
|
||||
*/
|
||||
std::optional<TileChangeset> changed_tile_chunks_since(const ImageTile *image_tile,
|
||||
const ChangesetID from_changeset)
|
||||
{
|
||||
std::optional<TileChangeset> changed_chunks = std::nullopt;
|
||||
for (int index = from_changeset - first_changeset_id; index < history.size(); index++) {
|
||||
if (!history[index].has_tile(image_tile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TileChangeset &tile_changeset = history[index][image_tile];
|
||||
if (!changed_chunks.has_value()) {
|
||||
changed_chunks = std::make_optional<TileChangeset>();
|
||||
changed_chunks->init_chunks(tile_changeset.chunk_x_len, tile_changeset.chunk_y_len);
|
||||
changed_chunks->tile_number = image_tile->tile_number;
|
||||
}
|
||||
|
||||
changed_chunks->merge(tile_changeset);
|
||||
}
|
||||
return changed_chunks;
|
||||
}
|
||||
};
|
||||
|
||||
static PartialUpdateRegister *image_partial_update_register_ensure(Image *image)
|
||||
{
|
||||
if (image->runtime.partial_update_register == nullptr) {
|
||||
PartialUpdateRegisterImpl *partial_update_register = MEM_new<PartialUpdateRegisterImpl>(
|
||||
__func__);
|
||||
image->runtime.partial_update_register = wrap(partial_update_register);
|
||||
}
|
||||
return image->runtime.partial_update_register;
|
||||
}
|
||||
|
||||
ePartialUpdateCollectResult BKE_image_partial_update_collect_changes(Image *image,
|
||||
PartialUpdateUser *user)
|
||||
{
|
||||
PartialUpdateUserImpl *user_impl = unwrap(user);
|
||||
#ifdef NDEBUG
|
||||
BLI_assert(image == user_impl->debug_image_);
|
||||
#endif
|
||||
|
||||
user_impl->clear_updated_regions();
|
||||
|
||||
PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image));
|
||||
partial_updater->ensure_empty_changeset();
|
||||
|
||||
if (!partial_updater->can_construct(user_impl->last_changeset_id)) {
|
||||
user_impl->last_changeset_id = partial_updater->last_changeset_id;
|
||||
return ePartialUpdateCollectResult::FullUpdateNeeded;
|
||||
}
|
||||
|
||||
/* Check if there are changes since last invocation for the user. */
|
||||
if (user_impl->last_changeset_id == partial_updater->last_changeset_id) {
|
||||
return ePartialUpdateCollectResult::NoChangesDetected;
|
||||
}
|
||||
|
||||
/* Collect changed tiles. */
|
||||
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
|
||||
std::optional<TileChangeset> changed_chunks = partial_updater->changed_tile_chunks_since(
|
||||
tile, user_impl->last_changeset_id);
|
||||
/* Check if chunks of this tile are dirty. */
|
||||
if (!changed_chunks.has_value()) {
|
||||
continue;
|
||||
}
|
||||
if (!changed_chunks->has_dirty_chunks()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Convert tiles in the changeset to rectangles that are dirty. */
|
||||
for (int chunk_y = 0; chunk_y < changed_chunks->chunk_y_len; chunk_y++) {
|
||||
for (int chunk_x = 0; chunk_x < changed_chunks->chunk_x_len; chunk_x++) {
|
||||
if (!changed_chunks->is_chunk_dirty(chunk_x, chunk_y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PartialUpdateRegion region;
|
||||
region.tile_number = tile->tile_number;
|
||||
BLI_rcti_init(®ion.region,
|
||||
chunk_x * CHUNK_SIZE,
|
||||
(chunk_x + 1) * CHUNK_SIZE,
|
||||
chunk_y * CHUNK_SIZE,
|
||||
(chunk_y + 1) * CHUNK_SIZE);
|
||||
user_impl->updated_regions.append_as(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
user_impl->last_changeset_id = partial_updater->last_changeset_id;
|
||||
return ePartialUpdateCollectResult::PartialChangesDetected;
|
||||
}
|
||||
|
||||
ePartialUpdateIterResult BKE_image_partial_update_get_next_change(PartialUpdateUser *user,
|
||||
PartialUpdateRegion *r_region)
|
||||
{
|
||||
PartialUpdateUserImpl *user_impl = unwrap(user);
|
||||
if (user_impl->updated_regions.is_empty()) {
|
||||
return ePartialUpdateIterResult::Finished;
|
||||
}
|
||||
PartialUpdateRegion region = user_impl->updated_regions.pop_last();
|
||||
*r_region = region;
|
||||
return ePartialUpdateIterResult::ChangeAvailable;
|
||||
}
|
||||
|
||||
} // namespace blender::bke::image::partial_update
|
||||
|
||||
extern "C" {
|
||||
|
||||
using namespace blender::bke::image::partial_update;
|
||||
|
||||
// TODO(jbakker): cleanup parameter.
|
||||
struct PartialUpdateUser *BKE_image_partial_update_create(const struct Image *image)
|
||||
{
|
||||
PartialUpdateUserImpl *user_impl = MEM_new<PartialUpdateUserImpl>(__func__);
|
||||
|
||||
#ifdef NDEBUG
|
||||
user_impl->debug_image_ = image;
|
||||
#else
|
||||
UNUSED_VARS(image);
|
||||
#endif
|
||||
|
||||
return wrap(user_impl);
|
||||
}
|
||||
|
||||
void BKE_image_partial_update_free(PartialUpdateUser *user)
|
||||
{
|
||||
PartialUpdateUserImpl *user_impl = unwrap(user);
|
||||
MEM_delete(user_impl);
|
||||
}
|
||||
|
||||
/* --- Image side --- */
|
||||
|
||||
void BKE_image_partial_update_register_free(Image *image)
|
||||
{
|
||||
PartialUpdateRegisterImpl *partial_update_register = unwrap(
|
||||
image->runtime.partial_update_register);
|
||||
if (partial_update_register) {
|
||||
MEM_delete(partial_update_register);
|
||||
}
|
||||
image->runtime.partial_update_register = nullptr;
|
||||
}
|
||||
|
||||
void BKE_image_partial_update_mark_region(Image *image,
|
||||
const ImageTile *image_tile,
|
||||
const ImBuf *image_buffer,
|
||||
const rcti *updated_region)
|
||||
{
|
||||
PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image));
|
||||
partial_updater->update_resolution(image_tile, image_buffer);
|
||||
partial_updater->mark_region(image_tile, updated_region);
|
||||
}
|
||||
|
||||
void BKE_image_partial_update_mark_full_update(Image *image)
|
||||
{
|
||||
PartialUpdateRegisterImpl *partial_updater = unwrap(image_partial_update_register_ensure(image));
|
||||
partial_updater->mark_full_update();
|
||||
}
|
||||
}
|
@@ -1,393 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2020 by Blender Foundation.
|
||||
*/
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "BKE_appdir.h"
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_partial_update.hh"
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_moviecache.h"
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender::bke::image::partial_update {
|
||||
|
||||
constexpr float black_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
||||
class ImagePartialUpdateTest : public testing::Test {
|
||||
protected:
|
||||
Main *bmain;
|
||||
Image *image;
|
||||
ImageTile *image_tile;
|
||||
ImageUser image_user = {nullptr};
|
||||
ImBuf *image_buffer;
|
||||
PartialUpdateUser *partial_update_user;
|
||||
|
||||
private:
|
||||
Image *create_test_image(int width, int height)
|
||||
{
|
||||
return BKE_image_add_generated(bmain,
|
||||
width,
|
||||
height,
|
||||
"Test Image",
|
||||
32,
|
||||
true,
|
||||
IMA_GENTYPE_BLANK,
|
||||
black_color,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
CLG_init();
|
||||
BKE_idtype_init();
|
||||
BKE_appdir_init();
|
||||
IMB_init();
|
||||
|
||||
bmain = BKE_main_new();
|
||||
/* Creating an image generates a mem-leak during tests. */
|
||||
image = create_test_image(1024, 1024);
|
||||
image_tile = BKE_image_get_tile(image, 0);
|
||||
image_buffer = BKE_image_acquire_ibuf(image, nullptr, nullptr);
|
||||
|
||||
partial_update_user = BKE_image_partial_update_create(image);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
BKE_image_release_ibuf(image, image_buffer, nullptr);
|
||||
BKE_image_partial_update_free(partial_update_user);
|
||||
BKE_main_free(bmain);
|
||||
|
||||
IMB_moviecache_destruct();
|
||||
IMB_exit();
|
||||
BKE_appdir_exit();
|
||||
CLG_exit();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ImagePartialUpdateTest, mark_full_update)
|
||||
{
|
||||
ePartialUpdateCollectResult result;
|
||||
/* First tile should always return a full update. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
/* Second invoke should now detect no changes. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
/* Mark full update */
|
||||
BKE_image_partial_update_mark_full_update(image);
|
||||
|
||||
/* Validate need full update followed by no changes. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
}
|
||||
|
||||
TEST_F(ImagePartialUpdateTest, mark_single_tile)
|
||||
{
|
||||
ePartialUpdateCollectResult result;
|
||||
/* First tile should always return a full update. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
/* Second invoke should now detect no changes. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
/* Mark region. */
|
||||
rcti region;
|
||||
BLI_rcti_init(®ion, 10, 20, 40, 50);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
|
||||
/* Partial Update should be available. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
/* Check tiles. */
|
||||
PartialUpdateRegion changed_region;
|
||||
ePartialUpdateIterResult iter_result;
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
|
||||
EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true);
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished);
|
||||
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
}
|
||||
|
||||
TEST_F(ImagePartialUpdateTest, mark_unconnected_tiles)
|
||||
{
|
||||
ePartialUpdateCollectResult result;
|
||||
/* First tile should always return a full update. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
/* Second invoke should now detect no changes. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
/* Mark region. */
|
||||
rcti region_a;
|
||||
BLI_rcti_init(®ion_a, 10, 20, 40, 50);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion_a);
|
||||
rcti region_b;
|
||||
BLI_rcti_init(®ion_b, 710, 720, 740, 750);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion_b);
|
||||
|
||||
/* Partial Update should be available. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
/* Check tiles. */
|
||||
PartialUpdateRegion changed_region;
|
||||
ePartialUpdateIterResult iter_result;
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
|
||||
EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion_b), true);
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
|
||||
EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion_a), true);
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished);
|
||||
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
}
|
||||
|
||||
TEST_F(ImagePartialUpdateTest, donot_mark_outside_image)
|
||||
{
|
||||
ePartialUpdateCollectResult result;
|
||||
/* First tile should always return a full update. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
/* Second invoke should now detect no changes. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
/* Mark region. */
|
||||
rcti region;
|
||||
/* Axis. */
|
||||
BLI_rcti_init(®ion, -100, 0, 50, 100);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
BLI_rcti_init(®ion, 1024, 1100, 50, 100);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
BLI_rcti_init(®ion, 50, 100, -100, 0);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
BLI_rcti_init(®ion, 50, 100, 1024, 1100);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
/* Diagonals. */
|
||||
BLI_rcti_init(®ion, -100, 0, -100, 0);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
BLI_rcti_init(®ion, -100, 0, 1024, 1100);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
BLI_rcti_init(®ion, 1024, 1100, -100, 0);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
BLI_rcti_init(®ion, 1024, 1100, 1024, 1100);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
}
|
||||
|
||||
TEST_F(ImagePartialUpdateTest, mark_inside_image)
|
||||
{
|
||||
ePartialUpdateCollectResult result;
|
||||
/* First tile should always return a full update. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
/* Second invoke should now detect no changes. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
/* Mark region. */
|
||||
rcti region;
|
||||
BLI_rcti_init(®ion, 0, 1, 0, 1);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
BLI_rcti_init(®ion, 1023, 1024, 0, 1);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
BLI_rcti_init(®ion, 1023, 1024, 1023, 1024);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
BLI_rcti_init(®ion, 1023, 1024, 0, 1);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
}
|
||||
|
||||
TEST_F(ImagePartialUpdateTest, sequential_mark_region)
|
||||
{
|
||||
ePartialUpdateCollectResult result;
|
||||
/* First tile should always return a full update. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
/* Second invoke should now detect no changes. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
{
|
||||
/* Mark region. */
|
||||
rcti region;
|
||||
BLI_rcti_init(®ion, 10, 20, 40, 50);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
|
||||
/* Partial Update should be available. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
/* Check tiles. */
|
||||
PartialUpdateRegion changed_region;
|
||||
ePartialUpdateIterResult iter_result;
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
|
||||
EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true);
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished);
|
||||
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
}
|
||||
|
||||
{
|
||||
/* Mark different region. */
|
||||
rcti region;
|
||||
BLI_rcti_init(®ion, 710, 720, 740, 750);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
|
||||
/* Partial Update should be available. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
/* Check tiles. */
|
||||
PartialUpdateRegion changed_region;
|
||||
ePartialUpdateIterResult iter_result;
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::ChangeAvailable);
|
||||
EXPECT_EQ(BLI_rcti_inside_rcti(&changed_region.region, ®ion), true);
|
||||
iter_result = BKE_image_partial_update_get_next_change(partial_update_user, &changed_region);
|
||||
EXPECT_EQ(iter_result, ePartialUpdateIterResult::Finished);
|
||||
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ImagePartialUpdateTest, mark_multiple_chunks)
|
||||
{
|
||||
ePartialUpdateCollectResult result;
|
||||
/* First tile should always return a full update. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
/* Second invoke should now detect no changes. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
/* Mark region. */
|
||||
rcti region;
|
||||
BLI_rcti_init(®ion, 300, 700, 300, 700);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
|
||||
/* Partial Update should be available. */
|
||||
result = BKE_image_partial_update_collect_changes(image, partial_update_user);
|
||||
EXPECT_EQ(result, ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
/* Check tiles. */
|
||||
PartialUpdateRegion changed_region;
|
||||
int num_chunks_found = 0;
|
||||
while (BKE_image_partial_update_get_next_change(partial_update_user, &changed_region) ==
|
||||
ePartialUpdateIterResult::ChangeAvailable) {
|
||||
BLI_rcti_isect(&changed_region.region, ®ion, nullptr);
|
||||
num_chunks_found++;
|
||||
}
|
||||
EXPECT_EQ(num_chunks_found, 4);
|
||||
}
|
||||
|
||||
TEST_F(ImagePartialUpdateTest, iterator)
|
||||
{
|
||||
PartialUpdateChecker<NoTileData> checker(image, &image_user, partial_update_user);
|
||||
/* First tile should always return a full update. */
|
||||
PartialUpdateChecker<NoTileData>::CollectResult changes = checker.collect_changes();
|
||||
EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::FullUpdateNeeded);
|
||||
/* Second invoke should now detect no changes. */
|
||||
changes = checker.collect_changes();
|
||||
EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::NoChangesDetected);
|
||||
|
||||
/* Mark region. */
|
||||
rcti region;
|
||||
BLI_rcti_init(®ion, 300, 700, 300, 700);
|
||||
BKE_image_partial_update_mark_region(image, image_tile, image_buffer, ®ion);
|
||||
|
||||
/* Partial Update should be available. */
|
||||
changes = checker.collect_changes();
|
||||
EXPECT_EQ(changes.get_result_code(), ePartialUpdateCollectResult::PartialChangesDetected);
|
||||
|
||||
/* Check tiles. */
|
||||
int num_tiles_found = 0;
|
||||
while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
|
||||
BLI_rcti_isect(&changes.changed_region.region, ®ion, nullptr);
|
||||
num_tiles_found++;
|
||||
}
|
||||
EXPECT_EQ(num_tiles_found, 4);
|
||||
}
|
||||
|
||||
} // namespace blender::bke::image::partial_update
|
@@ -420,7 +420,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
||||
/* calculate custom normals into loop_normals, then mirror first half into second half */
|
||||
|
||||
BKE_mesh_normals_loop_split(result->mvert,
|
||||
BKE_mesh_vertex_normals_ensure(result),
|
||||
BKE_mesh_vertex_normals_ensure(mesh),
|
||||
result->totvert,
|
||||
result->medge,
|
||||
result->totedge,
|
||||
@@ -428,7 +428,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
||||
loop_normals,
|
||||
totloop,
|
||||
result->mpoly,
|
||||
BKE_mesh_poly_normals_ensure(result),
|
||||
BKE_mesh_poly_normals_ensure(mesh),
|
||||
totpoly,
|
||||
true,
|
||||
mesh->smoothresh,
|
||||
|
@@ -107,20 +107,6 @@ struct float4x4 {
|
||||
return &values[0][0];
|
||||
}
|
||||
|
||||
float *operator[](const int64_t index)
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < 4);
|
||||
return &values[index][0];
|
||||
}
|
||||
|
||||
const float *operator[](const int64_t index) const
|
||||
{
|
||||
BLI_assert(index >= 0);
|
||||
BLI_assert(index < 4);
|
||||
return &values[index][0];
|
||||
}
|
||||
|
||||
using c_style_float4x4 = float[4][4];
|
||||
c_style_float4x4 &ptr()
|
||||
{
|
||||
|
@@ -404,7 +404,6 @@ void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]);
|
||||
|
||||
void scale_m3_fl(float R[3][3], float scale);
|
||||
void scale_m4_fl(float R[4][4], float scale);
|
||||
void scale_m4_v2(float R[4][4], const float scale[2]);
|
||||
|
||||
/**
|
||||
* This computes the overall volume scale factor of a transformation matrix.
|
||||
|
@@ -380,11 +380,6 @@ void BLI_path_normalize_unc_16(wchar_t *path_16);
|
||||
void BLI_path_normalize_unc(char *path_16, int maxlen);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns true if the given paths are equal.
|
||||
*/
|
||||
bool BLI_paths_equal(const char *p1, const char *p2);
|
||||
|
||||
/**
|
||||
* Appends a suffix to the string, fitting it before the extension
|
||||
*
|
||||
|
@@ -2296,17 +2296,6 @@ void scale_m4_fl(float R[4][4], float scale)
|
||||
R[3][0] = R[3][1] = R[3][2] = 0.0;
|
||||
}
|
||||
|
||||
void scale_m4_v2(float R[4][4], const float scale[2])
|
||||
{
|
||||
R[0][0] = scale[0];
|
||||
R[1][1] = scale[1];
|
||||
R[2][2] = R[3][3] = 1.0;
|
||||
R[0][1] = R[0][2] = R[0][3] = 0.0;
|
||||
R[1][0] = R[1][2] = R[1][3] = 0.0;
|
||||
R[2][0] = R[2][1] = R[2][3] = 0.0;
|
||||
R[3][0] = R[3][1] = R[3][2] = 0.0;
|
||||
}
|
||||
|
||||
void translate_m4(float mat[4][4], float Tx, float Ty, float Tz)
|
||||
{
|
||||
mat[3][0] += (Tx * mat[0][0] + Ty * mat[1][0] + Tz * mat[2][0]);
|
||||
|
@@ -1829,21 +1829,3 @@ void BLI_path_slash_native(char *path)
|
||||
BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BLI_paths_equal(const char *p1, const char *p2)
|
||||
{
|
||||
/* Normalize the paths so we can compare them. */
|
||||
char norm_p1[FILE_MAX];
|
||||
char norm_p2[FILE_MAX];
|
||||
|
||||
BLI_strncpy(norm_p1, p1, sizeof(norm_p1));
|
||||
BLI_strncpy(norm_p2, p2, sizeof(norm_p2));
|
||||
|
||||
BLI_path_slash_native(norm_p1);
|
||||
BLI_path_slash_native(norm_p2);
|
||||
|
||||
BLI_path_normalize(NULL, norm_p1);
|
||||
BLI_path_normalize(NULL, norm_p2);
|
||||
|
||||
return BLI_path_cmp(norm_p1, norm_p2) == 0;
|
||||
}
|
||||
|
@@ -227,17 +227,11 @@ set(SRC
|
||||
engines/eevee/eevee_lut.h
|
||||
engines/eevee/eevee_private.h
|
||||
engines/external/external_engine.h
|
||||
engines/image/image_batches.hh
|
||||
engines/image/image_drawing_mode.hh
|
||||
engines/image/image_drawing_mode_image_space.hh
|
||||
engines/image/image_engine.h
|
||||
engines/image/image_instance_data.hh
|
||||
engines/image/image_partial_updater.hh
|
||||
engines/image/image_private.hh
|
||||
engines/image/image_shader_params.hh
|
||||
engines/image/image_space_image.hh
|
||||
engines/image/image_space_node.hh
|
||||
engines/image/image_space.hh
|
||||
engines/image/image_wrappers.hh
|
||||
engines/workbench/workbench_engine.h
|
||||
engines/workbench/workbench_private.h
|
||||
engines/workbench/workbench_shader_shared.h
|
||||
|
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "image_texture_info.hh"
|
||||
|
||||
/** \brief Create GPUBatch for a IMAGE_ScreenSpaceTextureInfo. */
|
||||
class BatchUpdater {
|
||||
TextureInfo &info;
|
||||
|
||||
GPUVertFormat format = {0};
|
||||
int pos_id;
|
||||
int uv_id;
|
||||
|
||||
public:
|
||||
BatchUpdater(TextureInfo &info) : info(info)
|
||||
{
|
||||
}
|
||||
|
||||
void update_batch()
|
||||
{
|
||||
ensure_clear_batch();
|
||||
ensure_format();
|
||||
init_batch();
|
||||
}
|
||||
|
||||
void discard_batch()
|
||||
{
|
||||
GPU_BATCH_DISCARD_SAFE(info.batch);
|
||||
}
|
||||
|
||||
private:
|
||||
void ensure_clear_batch()
|
||||
{
|
||||
GPU_BATCH_CLEAR_SAFE(info.batch);
|
||||
if (info.batch == nullptr) {
|
||||
info.batch = GPU_batch_calloc();
|
||||
}
|
||||
}
|
||||
|
||||
void init_batch()
|
||||
{
|
||||
GPUVertBuf *vbo = create_vbo();
|
||||
GPU_batch_init_ex(info.batch, GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
GPUVertBuf *create_vbo()
|
||||
{
|
||||
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
|
||||
GPU_vertbuf_data_alloc(vbo, 4);
|
||||
float pos[4][2];
|
||||
fill_tri_fan_from_rctf(pos, info.clipping_bounds);
|
||||
float uv[4][2];
|
||||
fill_tri_fan_from_rctf(uv, info.uv_bounds);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
GPU_vertbuf_attr_set(vbo, pos_id, i, pos[i]);
|
||||
GPU_vertbuf_attr_set(vbo, uv_id, i, uv[i]);
|
||||
}
|
||||
|
||||
return vbo;
|
||||
}
|
||||
|
||||
static void fill_tri_fan_from_rctf(float result[4][2], rctf &rect)
|
||||
{
|
||||
result[0][0] = rect.xmin;
|
||||
result[0][1] = rect.ymin;
|
||||
result[1][0] = rect.xmax;
|
||||
result[1][1] = rect.ymin;
|
||||
result[2][0] = rect.xmax;
|
||||
result[2][1] = rect.ymax;
|
||||
result[3][0] = rect.xmin;
|
||||
result[3][1] = rect.ymax;
|
||||
}
|
||||
|
||||
void ensure_format()
|
||||
{
|
||||
if (format.attr_len == 0) {
|
||||
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
GPU_vertformat_attr_add(&format, "uv", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
|
||||
pos_id = GPU_vertformat_attr_id_get(&format, "pos");
|
||||
uv_id = GPU_vertformat_attr_id_get(&format, "uv");
|
||||
}
|
||||
}
|
||||
};
|
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_image_partial_update.hh"
|
||||
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "image_batches.hh"
|
||||
#include "image_private.hh"
|
||||
#include "image_wrappers.hh"
|
||||
|
||||
namespace blender::draw::image_engine {
|
||||
|
||||
constexpr float EPSILON_UV_BOUNDS = 0.00001f;
|
||||
|
||||
/**
|
||||
* \brief Screen space method using a single texture spawning the whole screen.
|
||||
*/
|
||||
struct OneTextureMethod {
|
||||
IMAGE_InstanceData *instance_data;
|
||||
|
||||
OneTextureMethod(IMAGE_InstanceData *instance_data) : instance_data(instance_data)
|
||||
{
|
||||
}
|
||||
|
||||
/** \brief Update the texture slot uv and screen space bounds. */
|
||||
void update_screen_space_bounds(const ARegion *region)
|
||||
{
|
||||
/* Create a single texture that covers the visible screen space. */
|
||||
BLI_rctf_init(
|
||||
&instance_data->texture_infos[0].clipping_bounds, 0, region->winx, 0, region->winy);
|
||||
instance_data->texture_infos[0].visible = true;
|
||||
|
||||
/* Mark the other textures as invalid. */
|
||||
for (int i = 1; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
|
||||
BLI_rctf_init_minmax(&instance_data->texture_infos[i].clipping_bounds);
|
||||
instance_data->texture_infos[i].visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
void update_uv_bounds(const ARegion *region)
|
||||
{
|
||||
TextureInfo &info = instance_data->texture_infos[0];
|
||||
if (!BLI_rctf_compare(&info.uv_bounds, ®ion->v2d.cur, EPSILON_UV_BOUNDS)) {
|
||||
info.uv_bounds = region->v2d.cur;
|
||||
info.dirty = true;
|
||||
}
|
||||
|
||||
/* Mark the other textures as invalid. */
|
||||
for (int i = 1; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
|
||||
BLI_rctf_init_minmax(&instance_data->texture_infos[i].clipping_bounds);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using namespace blender::bke::image::partial_update;
|
||||
|
||||
template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractDrawingMode {
|
||||
private:
|
||||
DRWPass *create_image_pass() const
|
||||
{
|
||||
/* Write depth is needed for background overlay rendering. Near depth is used for
|
||||
* transparency checker and Far depth is used for indicating the image size. */
|
||||
DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
|
||||
DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL);
|
||||
return DRW_pass_create("Image", state);
|
||||
}
|
||||
|
||||
void add_shgroups(const IMAGE_InstanceData *instance_data) const
|
||||
{
|
||||
const ShaderParameters &sh_params = instance_data->sh_params;
|
||||
GPUShader *shader = IMAGE_shader_image_get(false);
|
||||
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_create(shader, instance_data->passes.image_pass);
|
||||
DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", sh_params.far_near);
|
||||
DRW_shgroup_uniform_vec4_copy(shgrp, "color", ShaderParameters::color);
|
||||
DRW_shgroup_uniform_vec4_copy(shgrp, "shuffle", sh_params.shuffle);
|
||||
DRW_shgroup_uniform_int_copy(shgrp, "drawFlags", sh_params.flags);
|
||||
DRW_shgroup_uniform_bool_copy(shgrp, "imgPremultiplied", sh_params.use_premul_alpha);
|
||||
DRW_shgroup_uniform_vec2_copy(shgrp, "maxUv", instance_data->max_uv);
|
||||
float image_mat[4][4];
|
||||
unit_m4(image_mat);
|
||||
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
|
||||
const TextureInfo &info = instance_data->texture_infos[i];
|
||||
if (!info.visible) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
Should be space relative translation.
|
||||
image_mat[0][0] = info.clipping_bounds.xmax;
|
||||
image_mat[1][1] = info.clipping_bounds.ymax;
|
||||
*/
|
||||
|
||||
DRWShadingGroup *shgrp_sub = DRW_shgroup_create_sub(shgrp);
|
||||
DRW_shgroup_uniform_texture_ex(shgrp_sub, "imageTexture", info.texture, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_call_obmat(shgrp_sub, info.batch, image_mat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Update GPUTextures for drawing the image.
|
||||
*
|
||||
* GPUTextures that are marked dirty are rebuild. GPUTextures that aren't marked dirty are
|
||||
* updated with changed region of the image.
|
||||
*/
|
||||
void update_textures(IMAGE_InstanceData &instance_data,
|
||||
Image *image,
|
||||
ImageUser *image_user) const
|
||||
{
|
||||
PartialUpdateChecker<ImageTileData> checker(
|
||||
image, image_user, instance_data.partial_update.user);
|
||||
PartialUpdateChecker<ImageTileData>::CollectResult changes = checker.collect_changes();
|
||||
|
||||
switch (changes.get_result_code()) {
|
||||
case ePartialUpdateCollectResult::FullUpdateNeeded:
|
||||
instance_data.mark_all_texture_slots_dirty();
|
||||
break;
|
||||
case ePartialUpdateCollectResult::NoChangesDetected:
|
||||
break;
|
||||
case ePartialUpdateCollectResult::PartialChangesDetected:
|
||||
/* Partial update when wrap repeat is enabled is not supported. */
|
||||
if (instance_data.flags.do_tile_drawing) {
|
||||
instance_data.mark_all_texture_slots_dirty();
|
||||
}
|
||||
else {
|
||||
do_partial_update(changes, instance_data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
do_full_update_for_dirty_textures(instance_data, image_user);
|
||||
}
|
||||
|
||||
void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator,
|
||||
IMAGE_InstanceData &instance_data) const
|
||||
{
|
||||
while (iterator.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) {
|
||||
/* Quick exit when tile_buffer isn't availble. */
|
||||
if (iterator.tile_data.tile_buffer == nullptr) {
|
||||
continue;
|
||||
}
|
||||
ensure_float_buffer(*iterator.tile_data.tile_buffer);
|
||||
const float tile_width = static_cast<float>(iterator.tile_data.tile_buffer->x);
|
||||
const float tile_height = static_cast<float>(iterator.tile_data.tile_buffer->y);
|
||||
|
||||
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
|
||||
const TextureInfo &info = instance_data.texture_infos[i];
|
||||
/* Dirty images will receive a full update. No need to do a partial one now. */
|
||||
if (info.dirty) {
|
||||
continue;
|
||||
}
|
||||
if (!info.visible) {
|
||||
continue;
|
||||
}
|
||||
GPUTexture *texture = info.texture;
|
||||
const float texture_width = GPU_texture_width(texture);
|
||||
const float texture_height = GPU_texture_height(texture);
|
||||
// TODO
|
||||
// early bound check.
|
||||
ImageTileWrapper tile_accessor(iterator.tile_data.tile);
|
||||
float tile_offset_x = static_cast<float>(tile_accessor.get_tile_x_offset());
|
||||
float tile_offset_y = static_cast<float>(tile_accessor.get_tile_y_offset());
|
||||
rcti *changed_region_in_texel_space = &iterator.changed_region.region;
|
||||
rctf changed_region_in_uv_space;
|
||||
BLI_rctf_init(&changed_region_in_uv_space,
|
||||
static_cast<float>(changed_region_in_texel_space->xmin) /
|
||||
static_cast<float>(iterator.tile_data.tile_buffer->x) +
|
||||
tile_offset_x,
|
||||
static_cast<float>(changed_region_in_texel_space->xmax) /
|
||||
static_cast<float>(iterator.tile_data.tile_buffer->x) +
|
||||
tile_offset_x,
|
||||
static_cast<float>(changed_region_in_texel_space->ymin) /
|
||||
static_cast<float>(iterator.tile_data.tile_buffer->y) +
|
||||
tile_offset_y,
|
||||
static_cast<float>(changed_region_in_texel_space->ymax) /
|
||||
static_cast<float>(iterator.tile_data.tile_buffer->y) +
|
||||
tile_offset_y);
|
||||
rctf changed_overlapping_region_in_uv_space;
|
||||
const bool region_overlap = BLI_rctf_isect(
|
||||
&info.uv_bounds, &changed_region_in_uv_space, &changed_overlapping_region_in_uv_space);
|
||||
if (!region_overlap) {
|
||||
continue;
|
||||
}
|
||||
// convert the overlapping region to texel space and to ss_pixel space...
|
||||
// TODO: first convert to ss_pixel space as integer based. and from there go back to texel
|
||||
// space. But perhaps this isn't needed and we could use an extraction offset somehow.
|
||||
rcti gpu_texture_region_to_update;
|
||||
BLI_rcti_init(&gpu_texture_region_to_update,
|
||||
floor((changed_overlapping_region_in_uv_space.xmin - info.uv_bounds.xmin) *
|
||||
texture_width / BLI_rctf_size_x(&info.uv_bounds)),
|
||||
floor((changed_overlapping_region_in_uv_space.xmax - info.uv_bounds.xmin) *
|
||||
texture_width / BLI_rctf_size_x(&info.uv_bounds)),
|
||||
ceil((changed_overlapping_region_in_uv_space.ymin - info.uv_bounds.ymin) *
|
||||
texture_height / BLI_rctf_size_y(&info.uv_bounds)),
|
||||
ceil((changed_overlapping_region_in_uv_space.ymax - info.uv_bounds.ymin) *
|
||||
texture_height / BLI_rctf_size_y(&info.uv_bounds)));
|
||||
|
||||
rcti tile_region_to_extract;
|
||||
BLI_rcti_init(
|
||||
&tile_region_to_extract,
|
||||
floor((changed_overlapping_region_in_uv_space.xmin - tile_offset_x) * tile_width),
|
||||
floor((changed_overlapping_region_in_uv_space.xmax - tile_offset_x) * tile_width),
|
||||
ceil((changed_overlapping_region_in_uv_space.ymin - tile_offset_y) * tile_height),
|
||||
ceil((changed_overlapping_region_in_uv_space.ymax - tile_offset_y) * tile_height));
|
||||
|
||||
// Create an image buffer with a size
|
||||
// extract and scale into an imbuf
|
||||
const int texture_region_width = BLI_rcti_size_x(&gpu_texture_region_to_update);
|
||||
const int texture_region_height = BLI_rcti_size_y(&gpu_texture_region_to_update);
|
||||
|
||||
ImBuf extracted_buffer;
|
||||
IMB_initImBuf(
|
||||
&extracted_buffer, texture_region_width, texture_region_height, 32, IB_rectfloat);
|
||||
|
||||
int offset = 0;
|
||||
ImBuf *tile_buffer = iterator.tile_data.tile_buffer;
|
||||
for (int y = gpu_texture_region_to_update.ymin; y < gpu_texture_region_to_update.ymax;
|
||||
y++) {
|
||||
float yf = y / (float)texture_height;
|
||||
float v = info.uv_bounds.ymax * yf + info.uv_bounds.ymin * (1.0 - yf) - tile_offset_y;
|
||||
for (int x = gpu_texture_region_to_update.xmin; x < gpu_texture_region_to_update.xmax;
|
||||
x++) {
|
||||
float xf = x / (float)texture_width;
|
||||
float u = info.uv_bounds.xmax * xf + info.uv_bounds.xmin * (1.0 - xf) - tile_offset_x;
|
||||
nearest_interpolation_color(tile_buffer,
|
||||
nullptr,
|
||||
&extracted_buffer.rect_float[offset * 4],
|
||||
u * tile_buffer->x,
|
||||
v * tile_buffer->y);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
GPU_texture_update_sub(texture,
|
||||
GPU_DATA_FLOAT,
|
||||
extracted_buffer.rect_float,
|
||||
gpu_texture_region_to_update.xmin,
|
||||
gpu_texture_region_to_update.ymin,
|
||||
0,
|
||||
extracted_buffer.x,
|
||||
extracted_buffer.y,
|
||||
0);
|
||||
imb_freerectImbuf_all(&extracted_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void do_full_update_for_dirty_textures(IMAGE_InstanceData &instance_data,
|
||||
const ImageUser *image_user) const
|
||||
{
|
||||
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
|
||||
TextureInfo &info = instance_data.texture_infos[i];
|
||||
if (!info.dirty) {
|
||||
continue;
|
||||
}
|
||||
if (!info.visible) {
|
||||
continue;
|
||||
}
|
||||
do_full_update_gpu_texture(info, instance_data, image_user);
|
||||
}
|
||||
}
|
||||
|
||||
void do_full_update_gpu_texture(TextureInfo &info,
|
||||
IMAGE_InstanceData &instance_data,
|
||||
const ImageUser *image_user) const
|
||||
{
|
||||
|
||||
ImBuf texture_buffer;
|
||||
const int texture_width = GPU_texture_width(info.texture);
|
||||
const int texture_height = GPU_texture_height(info.texture);
|
||||
IMB_initImBuf(&texture_buffer, texture_width, texture_height, 0, IB_rectfloat);
|
||||
ImageUser tile_user = {0};
|
||||
if (image_user) {
|
||||
tile_user = *image_user;
|
||||
}
|
||||
|
||||
void *lock;
|
||||
|
||||
Image *image = instance_data.image;
|
||||
LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
|
||||
const ImageTileWrapper image_tile(image_tile_ptr);
|
||||
tile_user.tile = image_tile.get_tile_number();
|
||||
|
||||
ImBuf *tile_buffer = BKE_image_acquire_ibuf(image, &tile_user, &lock);
|
||||
if (tile_buffer == nullptr) {
|
||||
/* Couldn't load the image buffer of the tile. */
|
||||
continue;
|
||||
}
|
||||
do_full_update_texture_slot(instance_data, info, texture_buffer, *tile_buffer, image_tile);
|
||||
BKE_image_release_ibuf(image, tile_buffer, lock);
|
||||
}
|
||||
GPU_texture_update(info.texture, GPU_DATA_FLOAT, texture_buffer.rect_float);
|
||||
imb_freerectImbuf_all(&texture_buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Ensure that the float buffer of the given image buffer is available.
|
||||
*/
|
||||
void ensure_float_buffer(ImBuf &image_buffer) const
|
||||
{
|
||||
if (image_buffer.rect_float == nullptr) {
|
||||
IMB_float_from_rect(&image_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void do_full_update_texture_slot(const IMAGE_InstanceData &instance_data,
|
||||
const TextureInfo &texture_info,
|
||||
ImBuf &texture_buffer,
|
||||
ImBuf &tile_buffer,
|
||||
const ImageTileWrapper &image_tile) const
|
||||
{
|
||||
const int texture_width = texture_buffer.x;
|
||||
const int texture_height = texture_buffer.y;
|
||||
ensure_float_buffer(tile_buffer);
|
||||
|
||||
/* IMB_transform works in a non-consistent space. This should be documented or fixed!.
|
||||
* Construct a variant of the info_uv_to_texture that adds the texel space
|
||||
* transformation.*/
|
||||
float uv_to_texel[4][4];
|
||||
copy_m4_m4(uv_to_texel, instance_data.ss_to_texture);
|
||||
float scale[3] = {static_cast<float>(texture_width) / static_cast<float>(tile_buffer.x),
|
||||
static_cast<float>(texture_height) / static_cast<float>(tile_buffer.y),
|
||||
1.0f};
|
||||
rescale_m4(uv_to_texel, scale);
|
||||
uv_to_texel[3][0] += image_tile.get_tile_x_offset() / BLI_rctf_size_x(&texture_info.uv_bounds);
|
||||
uv_to_texel[3][1] += image_tile.get_tile_y_offset() / BLI_rctf_size_y(&texture_info.uv_bounds);
|
||||
uv_to_texel[3][0] *= texture_width;
|
||||
uv_to_texel[3][1] *= texture_height;
|
||||
invert_m4(uv_to_texel);
|
||||
|
||||
rctf crop_rect;
|
||||
rctf *crop_rect_ptr = nullptr;
|
||||
eIMBTransformMode transform_mode;
|
||||
if (instance_data.flags.do_tile_drawing) {
|
||||
transform_mode = IMB_TRANSFORM_MODE_WRAP_REPEAT;
|
||||
}
|
||||
else {
|
||||
BLI_rctf_init(&crop_rect, 0.0, tile_buffer.x, 0.0, tile_buffer.y);
|
||||
crop_rect_ptr = &crop_rect;
|
||||
transform_mode = IMB_TRANSFORM_MODE_CROP_SRC;
|
||||
}
|
||||
|
||||
IMB_transform(&tile_buffer,
|
||||
&texture_buffer,
|
||||
transform_mode,
|
||||
IMB_FILTER_NEAREST,
|
||||
uv_to_texel,
|
||||
crop_rect_ptr);
|
||||
}
|
||||
|
||||
public:
|
||||
void cache_init(IMAGE_Data *vedata) const override
|
||||
{
|
||||
IMAGE_InstanceData *instance_data = vedata->instance_data;
|
||||
instance_data->passes.image_pass = create_image_pass();
|
||||
}
|
||||
|
||||
void cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser) const override
|
||||
{
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
IMAGE_InstanceData *instance_data = vedata->instance_data;
|
||||
TextureMethod method(instance_data);
|
||||
|
||||
instance_data->partial_update.ensure_image(image);
|
||||
instance_data->max_uv_update();
|
||||
instance_data->clear_dirty_flag();
|
||||
|
||||
// Step: Find out which screen space textures are needed to draw on the screen. Remove the
|
||||
// screen space textures that aren't needed.
|
||||
const ARegion *region = draw_ctx->region;
|
||||
method.update_screen_space_bounds(region);
|
||||
method.update_uv_bounds(region);
|
||||
|
||||
// Step: Update the GPU textures based on the changes in the image.
|
||||
instance_data->update_gpu_texture_allocations();
|
||||
update_textures(*instance_data, image, iuser);
|
||||
|
||||
// Step: Add the GPU textures to the shgroup.
|
||||
instance_data->update_batches();
|
||||
add_shgroups(instance_data);
|
||||
}
|
||||
|
||||
void draw_finish(IMAGE_Data *UNUSED(vedata)) const override
|
||||
{
|
||||
}
|
||||
|
||||
void draw_scene(IMAGE_Data *vedata) const override
|
||||
{
|
||||
IMAGE_InstanceData *instance_data = vedata->instance_data;
|
||||
|
||||
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
||||
GPU_framebuffer_bind(dfbl->default_fb);
|
||||
static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0);
|
||||
|
||||
DRW_view_set_active(instance_data->view);
|
||||
DRW_draw_pass(instance_data->passes.image_pass);
|
||||
DRW_view_set_active(nullptr);
|
||||
}
|
||||
}; // namespace clipping
|
||||
|
||||
} // namespace blender::draw::image_engine
|
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "image_private.hh"
|
||||
|
||||
namespace blender::draw::image_engine {
|
||||
|
||||
class ImageSpaceDrawingMode : public AbstractDrawingMode {
|
||||
private:
|
||||
DRWPass *create_image_pass() const
|
||||
{
|
||||
/* Write depth is needed for background overlay rendering. Near depth is used for
|
||||
* transparency checker and Far depth is used for indicating the image size. */
|
||||
DRWState state = static_cast<DRWState>(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH |
|
||||
DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL);
|
||||
return DRW_pass_create("Image", state);
|
||||
}
|
||||
|
||||
void add_to_shgroup(AbstractSpaceAccessor *space,
|
||||
DRWShadingGroup *grp,
|
||||
const Image *image,
|
||||
const ImBuf *image_buffer) const
|
||||
{
|
||||
float image_mat[4][4];
|
||||
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const ARegion *region = draw_ctx->region;
|
||||
space->get_image_mat(image_buffer, region, image_mat);
|
||||
|
||||
GPUBatch *geom = DRW_cache_quad_get();
|
||||
|
||||
const float translate_x = image_mat[3][0];
|
||||
const float translate_y = image_mat[3][1];
|
||||
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
|
||||
const int tile_x = ((tile->tile_number - 1001) % 10);
|
||||
const int tile_y = ((tile->tile_number - 1001) / 10);
|
||||
image_mat[3][0] = (float)tile_x + translate_x;
|
||||
image_mat[3][1] = (float)tile_y + translate_y;
|
||||
DRW_shgroup_call_obmat(grp, geom, image_mat);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void cache_init(IMAGE_Data *vedata) const override
|
||||
{
|
||||
IMAGE_PassList *psl = vedata->psl;
|
||||
|
||||
psl->image_pass = create_image_pass();
|
||||
}
|
||||
|
||||
void cache_image(AbstractSpaceAccessor *space,
|
||||
IMAGE_Data *vedata,
|
||||
Image *image,
|
||||
ImageUser *iuser,
|
||||
ImBuf *image_buffer) const override
|
||||
{
|
||||
IMAGE_PassList *psl = vedata->psl;
|
||||
IMAGE_StorageList *stl = vedata->stl;
|
||||
IMAGE_PrivateData *pd = stl->pd;
|
||||
|
||||
GPUTexture *tex_tile_data = nullptr;
|
||||
space->get_gpu_textures(
|
||||
image, iuser, image_buffer, &pd->texture, &pd->owns_texture, &tex_tile_data);
|
||||
if (pd->texture == nullptr) {
|
||||
return;
|
||||
}
|
||||
const bool is_tiled_texture = tex_tile_data != nullptr;
|
||||
|
||||
ShaderParameters sh_params;
|
||||
sh_params.use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image,
|
||||
image_buffer);
|
||||
const DRWContextState *draw_ctx = DRW_context_state_get();
|
||||
const Scene *scene = draw_ctx->scene;
|
||||
if (scene->camera && scene->camera->type == OB_CAMERA) {
|
||||
Camera *camera = static_cast<Camera *>(scene->camera->data);
|
||||
copy_v2_fl2(sh_params.far_near, camera->clip_end, camera->clip_start);
|
||||
}
|
||||
space->get_shader_parameters(sh_params, image_buffer, is_tiled_texture);
|
||||
|
||||
GPUShader *shader = IMAGE_shader_image_get(is_tiled_texture);
|
||||
DRWShadingGroup *shgrp = DRW_shgroup_create(shader, psl->image_pass);
|
||||
if (is_tiled_texture) {
|
||||
DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, GPU_SAMPLER_DEFAULT);
|
||||
DRW_shgroup_uniform_texture(shgrp, "imageTileData", tex_tile_data);
|
||||
}
|
||||
else {
|
||||
DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, GPU_SAMPLER_DEFAULT);
|
||||
}
|
||||
DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", sh_params.far_near);
|
||||
DRW_shgroup_uniform_vec4_copy(shgrp, "color", ShaderParameters::color);
|
||||
DRW_shgroup_uniform_vec4_copy(shgrp, "shuffle", sh_params.shuffle);
|
||||
DRW_shgroup_uniform_int_copy(shgrp, "drawFlags", sh_params.flags);
|
||||
DRW_shgroup_uniform_bool_copy(shgrp, "imgPremultiplied", sh_params.use_premul_alpha);
|
||||
|
||||
add_to_shgroup(space, shgrp, image, image_buffer);
|
||||
}
|
||||
|
||||
void draw_finish(IMAGE_Data *vedata) const override
|
||||
{
|
||||
IMAGE_StorageList *stl = vedata->stl;
|
||||
IMAGE_PrivateData *pd = stl->pd;
|
||||
|
||||
if (pd->texture && pd->owns_texture) {
|
||||
GPU_texture_free(pd->texture);
|
||||
pd->owns_texture = false;
|
||||
}
|
||||
pd->texture = nullptr;
|
||||
}
|
||||
|
||||
void draw_scene(IMAGE_Data *vedata) const override
|
||||
{
|
||||
IMAGE_PassList *psl = vedata->psl;
|
||||
IMAGE_PrivateData *pd = vedata->stl->pd;
|
||||
|
||||
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
|
||||
GPU_framebuffer_bind(dfbl->default_fb);
|
||||
static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0);
|
||||
|
||||
DRW_view_set_active(pd->view);
|
||||
DRW_draw_pass(psl->image_pass);
|
||||
DRW_view_set_active(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::draw::image_engine
|
@@ -41,7 +41,7 @@
|
||||
|
||||
#include "GPU_batch.h"
|
||||
|
||||
#include "image_drawing_mode.hh"
|
||||
#include "image_drawing_mode_image_space.hh"
|
||||
#include "image_engine.h"
|
||||
#include "image_private.hh"
|
||||
#include "image_space_image.hh"
|
||||
@@ -68,7 +68,7 @@ template<
|
||||
*
|
||||
* Useful during development to switch between drawing implementations.
|
||||
*/
|
||||
typename DrawingMode = ScreenSpaceDrawingMode<OneTextureMethod>>
|
||||
typename DrawingMode = ImageSpaceDrawingMode>
|
||||
class ImageEngine {
|
||||
private:
|
||||
const DRWContextState *draw_ctx;
|
||||
@@ -86,58 +86,48 @@ class ImageEngine {
|
||||
|
||||
void cache_init()
|
||||
{
|
||||
IMAGE_InstanceData *instance_data = vedata->instance_data;
|
||||
drawing_mode.cache_init(vedata);
|
||||
IMAGE_StorageList *stl = vedata->stl;
|
||||
IMAGE_PrivateData *pd = stl->pd;
|
||||
|
||||
/* Setup full screen view matrix. */
|
||||
const ARegion *region = draw_ctx->region;
|
||||
float winmat[4][4], viewmat[4][4];
|
||||
orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0);
|
||||
unit_m4(winmat);
|
||||
instance_data->view = DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr);
|
||||
drawing_mode.cache_init(vedata);
|
||||
pd->view = nullptr;
|
||||
if (space->has_view_override()) {
|
||||
const ARegion *region = draw_ctx->region;
|
||||
pd->view = space->create_view_override(region);
|
||||
}
|
||||
}
|
||||
|
||||
void cache_populate()
|
||||
{
|
||||
IMAGE_InstanceData *instance_data = vedata->instance_data;
|
||||
IMAGE_StorageList *stl = vedata->stl;
|
||||
IMAGE_PrivateData *pd = stl->pd;
|
||||
Main *bmain = CTX_data_main(draw_ctx->evil_C);
|
||||
instance_data->image = space->get_image(bmain);
|
||||
if (instance_data->image == nullptr) {
|
||||
pd->image = space->get_image(bmain);
|
||||
if (pd->image == nullptr) {
|
||||
/* Early exit, nothing to draw. */
|
||||
return;
|
||||
}
|
||||
instance_data->flags.do_tile_drawing = instance_data->image->source != IMA_SRC_TILED &&
|
||||
space->use_tile_drawing();
|
||||
void *lock;
|
||||
ImBuf *image_buffer = space->acquire_image_buffer(instance_data->image, &lock);
|
||||
|
||||
/* Setup the matrix to go from screen UV coordinates to UV texture space coordinates. */
|
||||
float image_resolution[2] = {image_buffer ? image_buffer->x : 1024.0f,
|
||||
image_buffer ? image_buffer->y : 1024.0f};
|
||||
space->init_ss_to_texture_matrix(
|
||||
draw_ctx->region, image_resolution, instance_data->ss_to_texture);
|
||||
|
||||
const Scene *scene = DRW_context_state_get()->scene;
|
||||
instance_data->sh_params.update(space.get(), scene, instance_data->image, image_buffer);
|
||||
space->release_buffer(instance_data->image, image_buffer, lock);
|
||||
|
||||
pd->ibuf = space->acquire_image_buffer(pd->image, &pd->lock);
|
||||
ImageUser *iuser = space->get_image_user();
|
||||
drawing_mode.cache_image(vedata, instance_data->image, iuser);
|
||||
drawing_mode.cache_image(space.get(), vedata, pd->image, iuser, pd->ibuf);
|
||||
}
|
||||
|
||||
void draw_finish()
|
||||
{
|
||||
drawing_mode.draw_finish(vedata);
|
||||
|
||||
IMAGE_InstanceData *instance_data = vedata->instance_data;
|
||||
instance_data->image = nullptr;
|
||||
IMAGE_StorageList *stl = vedata->stl;
|
||||
IMAGE_PrivateData *pd = stl->pd;
|
||||
space->release_buffer(pd->image, pd->ibuf, pd->lock);
|
||||
pd->image = nullptr;
|
||||
pd->ibuf = nullptr;
|
||||
}
|
||||
|
||||
void draw_scene()
|
||||
{
|
||||
drawing_mode.draw_scene(vedata);
|
||||
}
|
||||
}; // namespace blender::draw::image_engine
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Engine Callbacks
|
||||
@@ -147,9 +137,15 @@ static void IMAGE_engine_init(void *ved)
|
||||
{
|
||||
IMAGE_shader_library_ensure();
|
||||
IMAGE_Data *vedata = (IMAGE_Data *)ved;
|
||||
if (vedata->instance_data == nullptr) {
|
||||
vedata->instance_data = MEM_new<IMAGE_InstanceData>(__func__);
|
||||
IMAGE_StorageList *stl = vedata->stl;
|
||||
if (!stl->pd) {
|
||||
stl->pd = static_cast<IMAGE_PrivateData *>(MEM_callocN(sizeof(IMAGE_PrivateData), __func__));
|
||||
}
|
||||
IMAGE_PrivateData *pd = stl->pd;
|
||||
|
||||
pd->ibuf = nullptr;
|
||||
pd->lock = nullptr;
|
||||
pd->texture = nullptr;
|
||||
}
|
||||
|
||||
static void IMAGE_cache_init(void *vedata)
|
||||
@@ -178,12 +174,6 @@ static void IMAGE_engine_free()
|
||||
IMAGE_shader_free();
|
||||
}
|
||||
|
||||
static void IMAGE_instance_free(void *_instance_data)
|
||||
{
|
||||
IMAGE_InstanceData *instance_data = reinterpret_cast<IMAGE_InstanceData *>(_instance_data);
|
||||
MEM_delete(instance_data);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
static const DrawEngineDataSize IMAGE_data_size = DRW_VIEWPORT_DATA_SIZE(IMAGE_Data);
|
||||
@@ -201,7 +191,7 @@ DrawEngineType draw_engine_image_type = {
|
||||
&IMAGE_data_size, /* vedata_size */
|
||||
&IMAGE_engine_init, /* engine_init */
|
||||
&IMAGE_engine_free, /* engine_free */
|
||||
&IMAGE_instance_free, /* instance_free */
|
||||
nullptr, /* instance_free */
|
||||
&IMAGE_cache_init, /* cache_init */
|
||||
&IMAGE_cache_populate, /* cache_populate */
|
||||
nullptr, /* cache_finish */
|
||||
|
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "image_batches.hh"
|
||||
#include "image_partial_updater.hh"
|
||||
#include "image_private.hh"
|
||||
#include "image_shader_params.hh"
|
||||
#include "image_texture_info.hh"
|
||||
#include "image_wrappers.hh"
|
||||
|
||||
#include "DRW_render.h"
|
||||
|
||||
/**
|
||||
* \brief max allowed textures to use by the ScreenSpaceDrawingMode.
|
||||
*
|
||||
* 4 textures are used to reduce uploading screen space textures when translating the image.
|
||||
*/
|
||||
constexpr int SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN = 4;
|
||||
|
||||
struct IMAGE_InstanceData {
|
||||
struct Image *image;
|
||||
|
||||
PartialImageUpdater partial_update;
|
||||
|
||||
struct DRWView *view;
|
||||
ShaderParameters sh_params;
|
||||
struct {
|
||||
/**
|
||||
* \brief should we perform tiled drawing (wrap repeat).
|
||||
*
|
||||
* Option is true when image is capable of tile drawing (image is not tile) and the tiled
|
||||
* option is set in the space.
|
||||
*/
|
||||
bool do_tile_drawing : 1;
|
||||
} flags;
|
||||
|
||||
struct {
|
||||
DRWPass *image_pass;
|
||||
} passes;
|
||||
|
||||
/** \brief Transform matrix to convert a normalized screen space coordinates to texture space. */
|
||||
float ss_to_texture[4][4];
|
||||
TextureInfo texture_infos[SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN];
|
||||
|
||||
/**
|
||||
* \brief Maximum uv's that are on the border of the image.
|
||||
*
|
||||
* Larger UV coordinates would be drawn as a border. */
|
||||
float max_uv[2];
|
||||
|
||||
public:
|
||||
void max_uv_update()
|
||||
{
|
||||
copy_v2_fl2(max_uv, 1.0f, 1.0);
|
||||
LISTBASE_FOREACH (ImageTile *, image_tile_ptr, &image->tiles) {
|
||||
ImageTileWrapper image_tile(image_tile_ptr);
|
||||
max_uv[0] = max_ii(max_uv[0], image_tile.get_tile_x_offset() + 1);
|
||||
max_uv[1] = max_ii(max_uv[1], image_tile.get_tile_y_offset() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void clear_dirty_flag()
|
||||
{
|
||||
reset_dirty_flag(false);
|
||||
}
|
||||
void mark_all_texture_slots_dirty()
|
||||
{
|
||||
reset_dirty_flag(true);
|
||||
}
|
||||
|
||||
void update_gpu_texture_allocations()
|
||||
{
|
||||
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
|
||||
TextureInfo &info = texture_infos[i];
|
||||
const bool is_allocated = info.texture != nullptr;
|
||||
const bool is_visible = info.visible;
|
||||
const bool should_be_freed = !is_visible && is_allocated;
|
||||
const bool should_be_created = is_visible && !is_allocated;
|
||||
|
||||
if (should_be_freed) {
|
||||
GPU_texture_free(info.texture);
|
||||
info.texture = nullptr;
|
||||
}
|
||||
|
||||
if (should_be_created) {
|
||||
DRW_texture_ensure_fullscreen_2d(
|
||||
&info.texture, GPU_RGBA16F, static_cast<DRWTextureFlag>(0));
|
||||
}
|
||||
info.dirty |= should_be_created;
|
||||
}
|
||||
}
|
||||
|
||||
void update_batches()
|
||||
{
|
||||
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
|
||||
TextureInfo &info = texture_infos[i];
|
||||
if (!info.dirty) {
|
||||
continue;
|
||||
}
|
||||
BatchUpdater batch_updater(info);
|
||||
batch_updater.update_batch();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/** \brief Set dirty flag of all texture slots to the given value. */
|
||||
void reset_dirty_flag(bool new_value)
|
||||
{
|
||||
for (int i = 0; i < SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN; i++) {
|
||||
texture_infos[i].dirty = new_value;
|
||||
}
|
||||
}
|
||||
};
|
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_partial_update.hh"
|
||||
|
||||
struct PartialImageUpdater {
|
||||
struct PartialUpdateUser *user;
|
||||
const struct Image *image;
|
||||
|
||||
/**
|
||||
* \brief Ensure that there is a partial update user for the given image.
|
||||
*/
|
||||
void ensure_image(const Image *image)
|
||||
{
|
||||
if (!is_valid(image)) {
|
||||
free();
|
||||
create(image);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~PartialImageUpdater()
|
||||
{
|
||||
free();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief check if the partial update user can still be used for the given image.
|
||||
*
|
||||
* When switching to a different image the partial update user should be recreated.
|
||||
*/
|
||||
bool is_valid(const Image *new_image) const
|
||||
{
|
||||
if (image != new_image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return user != nullptr;
|
||||
}
|
||||
|
||||
void create(const Image *image)
|
||||
{
|
||||
BLI_assert(user == nullptr);
|
||||
user = BKE_image_partial_update_create(image);
|
||||
image = image;
|
||||
}
|
||||
|
||||
void free()
|
||||
{
|
||||
if (user != nullptr) {
|
||||
BKE_image_partial_update_free(user);
|
||||
user = nullptr;
|
||||
image = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
@@ -24,11 +24,6 @@
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BKE_image.h"
|
||||
|
||||
#include "image_instance_data.hh"
|
||||
#include "image_texture_info.hh"
|
||||
|
||||
/* Forward declarations */
|
||||
extern "C" {
|
||||
struct GPUTexture;
|
||||
@@ -40,13 +35,32 @@ struct Image;
|
||||
|
||||
namespace blender::draw::image_engine {
|
||||
|
||||
/* GPUViewport.storage
|
||||
* Is freed every time the viewport engine changes. */
|
||||
struct IMAGE_PassList {
|
||||
DRWPass *image_pass;
|
||||
};
|
||||
|
||||
struct IMAGE_PrivateData {
|
||||
void *lock;
|
||||
struct ImBuf *ibuf;
|
||||
struct Image *image;
|
||||
struct DRWView *view;
|
||||
|
||||
struct GPUTexture *texture;
|
||||
bool owns_texture;
|
||||
};
|
||||
|
||||
struct IMAGE_StorageList {
|
||||
IMAGE_PrivateData *pd;
|
||||
};
|
||||
|
||||
struct IMAGE_Data {
|
||||
void *engine_type;
|
||||
DRWViewportEmptyList *fbl;
|
||||
DRWViewportEmptyList *txl;
|
||||
DRWViewportEmptyList *psl;
|
||||
DRWViewportEmptyList *stl;
|
||||
IMAGE_InstanceData *instance_data;
|
||||
IMAGE_PassList *psl;
|
||||
IMAGE_StorageList *stl;
|
||||
};
|
||||
|
||||
/* Shader parameters. */
|
||||
@@ -55,6 +69,105 @@ struct IMAGE_Data {
|
||||
#define IMAGE_DRAW_FLAG_SHUFFLING (1 << 2)
|
||||
#define IMAGE_DRAW_FLAG_DEPTH (1 << 3)
|
||||
#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4)
|
||||
#define IMAGE_DRAW_FLAG_USE_WORLD_POS (1 << 5)
|
||||
|
||||
struct ShaderParameters {
|
||||
constexpr static float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
|
||||
int flags = 0;
|
||||
float shuffle[4];
|
||||
float far_near[2];
|
||||
bool use_premul_alpha = false;
|
||||
|
||||
ShaderParameters()
|
||||
{
|
||||
copy_v4_fl(shuffle, 1.0f);
|
||||
copy_v2_fl2(far_near, 100.0f, 0.0f);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Space accessor.
|
||||
*
|
||||
* Image engine is used to draw the images inside multiple spaces \see SpaceLink.
|
||||
* The AbstractSpaceAccessor is an interface to communicate with a space.
|
||||
*/
|
||||
class AbstractSpaceAccessor {
|
||||
public:
|
||||
virtual ~AbstractSpaceAccessor() = default;
|
||||
|
||||
/**
|
||||
* Return the active image of the space.
|
||||
*
|
||||
* The returned image will be drawn in the space.
|
||||
*
|
||||
* The return value is optional.
|
||||
*/
|
||||
virtual Image *get_image(Main *bmain) = 0;
|
||||
|
||||
/**
|
||||
* Return the #ImageUser of the space.
|
||||
*
|
||||
* The return value is optional.
|
||||
*/
|
||||
virtual ImageUser *get_image_user() = 0;
|
||||
|
||||
/**
|
||||
* Acquire the image buffer of the image.
|
||||
*
|
||||
* \param image: Image to get the buffer from. Image is the same as returned from the #get_image
|
||||
* member.
|
||||
* \param lock: pointer to a lock object.
|
||||
* \return Image buffer of the given image.
|
||||
*/
|
||||
virtual ImBuf *acquire_image_buffer(Image *image, void **lock) = 0;
|
||||
|
||||
/**
|
||||
* Release a previous locked image from #acquire_image_buffer.
|
||||
*/
|
||||
virtual void release_buffer(Image *image, ImBuf *image_buffer, void *lock) = 0;
|
||||
|
||||
/**
|
||||
* Update the r_shader_parameters with space specific settings.
|
||||
*
|
||||
* Only update the #ShaderParameters.flags and #ShaderParameters.shuffle. Other parameters
|
||||
* are updated inside the image engine.
|
||||
*/
|
||||
virtual void get_shader_parameters(ShaderParameters &r_shader_parameters,
|
||||
ImBuf *image_buffer,
|
||||
bool is_tiled) = 0;
|
||||
|
||||
/**
|
||||
* Retrieve the gpu textures to draw.
|
||||
*/
|
||||
virtual void get_gpu_textures(Image *image,
|
||||
ImageUser *iuser,
|
||||
ImBuf *image_buffer,
|
||||
GPUTexture **r_gpu_texture,
|
||||
bool *r_owns_texture,
|
||||
GPUTexture **r_tex_tile_data) = 0;
|
||||
|
||||
/**
|
||||
* Does this space override the view.
|
||||
* When so this member should return true and the create_view_override must return the view to
|
||||
* use during drawing.
|
||||
*/
|
||||
virtual bool has_view_override() const = 0;
|
||||
|
||||
/**
|
||||
* Override the view for drawing.
|
||||
* Should match #has_view_override.
|
||||
*/
|
||||
virtual DRWView *create_view_override(const ARegion *UNUSED(region)) = 0;
|
||||
|
||||
/**
|
||||
* Initialize the matrix that will be used to draw the image. The matrix will be send as object
|
||||
* matrix to the drawing pipeline.
|
||||
*/
|
||||
virtual void get_image_mat(const ImBuf *image_buffer,
|
||||
const ARegion *region,
|
||||
float r_mat[4][4]) const = 0;
|
||||
}; // namespace blender::draw::image_engine
|
||||
|
||||
/**
|
||||
* Abstract class for a drawing mode of the image engine.
|
||||
@@ -66,7 +179,11 @@ class AbstractDrawingMode {
|
||||
public:
|
||||
virtual ~AbstractDrawingMode() = default;
|
||||
virtual void cache_init(IMAGE_Data *vedata) const = 0;
|
||||
virtual void cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser) const = 0;
|
||||
virtual void cache_image(AbstractSpaceAccessor *space,
|
||||
IMAGE_Data *vedata,
|
||||
Image *image,
|
||||
ImageUser *iuser,
|
||||
ImBuf *image_buffer) const = 0;
|
||||
virtual void draw_scene(IMAGE_Data *vedata) const = 0;
|
||||
virtual void draw_finish(IMAGE_Data *vedata) const = 0;
|
||||
};
|
||||
|
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "BKE_image.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
|
||||
#include "image_space.hh"
|
||||
|
||||
struct ShaderParameters {
|
||||
constexpr static float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
|
||||
int flags = 0;
|
||||
float shuffle[4];
|
||||
float far_near[2];
|
||||
bool use_premul_alpha = false;
|
||||
|
||||
void update(AbstractSpaceAccessor *space, const Scene *scene, Image *image, ImBuf *image_buffer)
|
||||
{
|
||||
copy_v4_fl(shuffle, 1.0f);
|
||||
copy_v2_fl2(far_near, 100.0f, 0.0f);
|
||||
|
||||
use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
|
||||
|
||||
if (scene->camera && scene->camera->type == OB_CAMERA) {
|
||||
Camera *camera = static_cast<Camera *>(scene->camera->data);
|
||||
copy_v2_fl2(far_near, camera->clip_end, camera->clip_start);
|
||||
}
|
||||
const bool is_tiled_image = (image->source == IMA_SRC_TILED);
|
||||
space->get_shader_parameters(*this, image_buffer, is_tiled_image);
|
||||
}
|
||||
};
|
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class ShaderParameters;
|
||||
|
||||
/**
|
||||
* Space accessor.
|
||||
*
|
||||
* Image engine is used to draw the images inside multiple spaces \see SpaceLink.
|
||||
* The AbstractSpaceAccessor is an interface to communicate with a space.
|
||||
*/
|
||||
class AbstractSpaceAccessor {
|
||||
public:
|
||||
virtual ~AbstractSpaceAccessor() = default;
|
||||
|
||||
/**
|
||||
* Return the active image of the space.
|
||||
*
|
||||
* The returned image will be drawn in the space.
|
||||
*
|
||||
* The return value is optional.
|
||||
*/
|
||||
virtual Image *get_image(Main *bmain) = 0;
|
||||
|
||||
/**
|
||||
* Return the #ImageUser of the space.
|
||||
*
|
||||
* The return value is optional.
|
||||
*/
|
||||
virtual ImageUser *get_image_user() = 0;
|
||||
|
||||
/**
|
||||
* Acquire the image buffer of the image.
|
||||
*
|
||||
* \param image: Image to get the buffer from. Image is the same as returned from the #get_image
|
||||
* member.
|
||||
* \param lock: pointer to a lock object.
|
||||
* \return Image buffer of the given image.
|
||||
*/
|
||||
virtual ImBuf *acquire_image_buffer(Image *image, void **lock) = 0;
|
||||
|
||||
/**
|
||||
* Release a previous locked image from #acquire_image_buffer.
|
||||
*/
|
||||
virtual void release_buffer(Image *image, ImBuf *image_buffer, void *lock) = 0;
|
||||
|
||||
/**
|
||||
* Update the r_shader_parameters with space specific settings.
|
||||
*
|
||||
* Only update the #ShaderParameters.flags and #ShaderParameters.shuffle. Other parameters
|
||||
* are updated inside the image engine.
|
||||
*/
|
||||
virtual void get_shader_parameters(ShaderParameters &r_shader_parameters,
|
||||
ImBuf *image_buffer,
|
||||
bool is_tiled) = 0;
|
||||
|
||||
/**
|
||||
* Retrieve the gpu textures to draw.
|
||||
*/
|
||||
virtual void get_gpu_textures(Image *image,
|
||||
ImageUser *iuser,
|
||||
ImBuf *image_buffer,
|
||||
GPUTexture **r_gpu_texture,
|
||||
bool *r_owns_texture,
|
||||
GPUTexture **r_tex_tile_data) = 0;
|
||||
|
||||
/** \brief Is (wrap) repeat option enabled in the space. */
|
||||
virtual bool use_tile_drawing() const = 0;
|
||||
|
||||
/**
|
||||
* \brief Initialize r_uv_to_texture matrix to transform from normalized screen space coordinates
|
||||
* (0..1) to texture space UV coordinates.
|
||||
*/
|
||||
virtual void init_ss_to_texture_matrix(const ARegion *region,
|
||||
const float image_resolution[2],
|
||||
float r_uv_to_texture[4][4]) const = 0;
|
||||
|
||||
}; // namespace blender::draw::image_engine
|
@@ -61,6 +61,7 @@ class SpaceImageAccessor : public AbstractSpaceAccessor {
|
||||
const int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(image_buffer);
|
||||
const bool do_repeat = (!is_tiled) && ((sima->flag & SI_DRAW_TILE) != 0);
|
||||
SET_FLAG_FROM_TEST(r_shader_parameters.flags, do_repeat, IMAGE_DRAW_FLAG_DO_REPEAT);
|
||||
SET_FLAG_FROM_TEST(r_shader_parameters.flags, is_tiled, IMAGE_DRAW_FLAG_USE_WORLD_POS);
|
||||
if ((sima_flag & SI_USE_ALPHA) != 0) {
|
||||
/* Show RGBA */
|
||||
r_shader_parameters.flags |= IMAGE_DRAW_FLAG_SHOW_ALPHA | IMAGE_DRAW_FLAG_APPLY_ALPHA;
|
||||
@@ -101,6 +102,15 @@ class SpaceImageAccessor : public AbstractSpaceAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
bool has_view_override() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
DRWView *create_view_override(const ARegion *UNUSED(region)) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void get_gpu_textures(Image *image,
|
||||
ImageUser *iuser,
|
||||
ImBuf *image_buffer,
|
||||
@@ -161,26 +171,11 @@ class SpaceImageAccessor : public AbstractSpaceAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
bool use_tile_drawing() const override
|
||||
void get_image_mat(const ImBuf *UNUSED(image_buffer),
|
||||
const ARegion *UNUSED(region),
|
||||
float r_mat[4][4]) const override
|
||||
{
|
||||
return (sima->flag & SI_DRAW_TILE) != 0;
|
||||
}
|
||||
|
||||
void init_ss_to_texture_matrix(const ARegion *region,
|
||||
const float UNUSED(image_resolution[2]),
|
||||
float r_uv_to_texture[4][4]) const override
|
||||
{
|
||||
// TODO: I remember that there was a function for this somewhere.
|
||||
unit_m4(r_uv_to_texture);
|
||||
float scale_x = 1.0 / BLI_rctf_size_x(®ion->v2d.cur);
|
||||
float scale_y = 1.0 / BLI_rctf_size_y(®ion->v2d.cur);
|
||||
float translate_x = scale_x * -region->v2d.cur.xmin;
|
||||
float translate_y = scale_y * -region->v2d.cur.ymin;
|
||||
|
||||
r_uv_to_texture[0][0] = scale_x;
|
||||
r_uv_to_texture[1][1] = scale_y;
|
||||
r_uv_to_texture[3][0] = translate_x;
|
||||
r_uv_to_texture[3][1] = translate_y;
|
||||
unit_m4(r_mat);
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -54,6 +54,20 @@ class SpaceNodeAccessor : public AbstractSpaceAccessor {
|
||||
BKE_image_release_ibuf(image, ibuf, lock);
|
||||
}
|
||||
|
||||
bool has_view_override() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DRWView *create_view_override(const ARegion *region) override
|
||||
{
|
||||
/* Setup a screen pixel view. The backdrop of the node editor doesn't follow the region. */
|
||||
float winmat[4][4], viewmat[4][4];
|
||||
orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0);
|
||||
unit_m4(winmat);
|
||||
return DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void get_shader_parameters(ShaderParameters &r_shader_parameters,
|
||||
ImBuf *ibuf,
|
||||
bool UNUSED(is_tiled)) override
|
||||
@@ -106,33 +120,18 @@ class SpaceNodeAccessor : public AbstractSpaceAccessor {
|
||||
*r_tex_tile_data = nullptr;
|
||||
}
|
||||
|
||||
bool use_tile_drawing() const override
|
||||
void get_image_mat(const ImBuf *image_buffer,
|
||||
const ARegion *region,
|
||||
float r_mat[4][4]) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
unit_m4(r_mat);
|
||||
const float ibuf_width = image_buffer->x;
|
||||
const float ibuf_height = image_buffer->y;
|
||||
|
||||
/**
|
||||
* The backdrop of the node editor isn't drawn in screen space UV space. But is locked with the
|
||||
* screen.
|
||||
*/
|
||||
void init_ss_to_texture_matrix(const ARegion *region,
|
||||
const float image_resolution[2],
|
||||
float r_uv_to_texture[4][4]) const override
|
||||
{
|
||||
unit_m4(r_uv_to_texture);
|
||||
float display_resolution[2];
|
||||
mul_v2_v2fl(display_resolution, image_resolution, snode->zoom);
|
||||
const float scale_x = display_resolution[0] / region->winx;
|
||||
const float scale_y = display_resolution[1] / region->winy;
|
||||
const float translate_x = ((region->winx - display_resolution[0]) * 0.5f + snode->xof) /
|
||||
region->winx;
|
||||
const float translate_y = ((region->winy - display_resolution[1]) * 0.5f + snode->yof) /
|
||||
region->winy;
|
||||
|
||||
r_uv_to_texture[0][0] = scale_x;
|
||||
r_uv_to_texture[1][1] = scale_y;
|
||||
r_uv_to_texture[3][0] = translate_x;
|
||||
r_uv_to_texture[3][1] = translate_y;
|
||||
r_mat[0][0] = ibuf_width * snode->zoom;
|
||||
r_mat[1][1] = ibuf_height * snode->zoom;
|
||||
r_mat[3][0] = (region->winx - snode->zoom * ibuf_width) / 2 + snode->xof;
|
||||
r_mat[3][1] = (region->winy - snode->zoom * ibuf_height) / 2 + snode->yof;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2020, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
struct TextureInfo {
|
||||
/**
|
||||
* \brief Is the texture clipped.
|
||||
*
|
||||
* Resources of clipped textures are freed and ignored when performing partial updates.
|
||||
*/
|
||||
bool visible : 1;
|
||||
|
||||
/**
|
||||
* \brief does this texture need a full update.
|
||||
*
|
||||
* When set to false the texture can be updated using a partial update.
|
||||
*/
|
||||
bool dirty : 1;
|
||||
|
||||
/** \brief area of the texture in screen space. */
|
||||
rctf clipping_bounds;
|
||||
/** \brief uv area of the texture. */
|
||||
rctf uv_bounds;
|
||||
|
||||
/**
|
||||
* \brief Batch to draw the associated texton the screen.
|
||||
*
|
||||
* contans a VBO with `pos` and 'uv'.
|
||||
* `pos` (2xF32) is relative to the origin of the space.
|
||||
* `uv` (2xF32) reflect the uv bounds.
|
||||
*/
|
||||
GPUBatch *batch;
|
||||
|
||||
/**
|
||||
* \brief GPU Texture for a partial region of the image editor.
|
||||
*/
|
||||
GPUTexture *texture;
|
||||
|
||||
~TextureInfo()
|
||||
{
|
||||
if (batch != nullptr) {
|
||||
GPU_batch_discard(batch);
|
||||
batch = nullptr;
|
||||
}
|
||||
|
||||
if (texture != nullptr) {
|
||||
GPU_texture_free(texture);
|
||||
texture = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2020, Blender Foundation.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup draw_engine
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
struct ImageTileWrapper {
|
||||
ImageTile *image_tile;
|
||||
ImageTileWrapper(ImageTile *image_tile) : image_tile(image_tile)
|
||||
{
|
||||
}
|
||||
|
||||
int get_tile_number() const
|
||||
{
|
||||
return image_tile->tile_number;
|
||||
}
|
||||
|
||||
int get_tile_x_offset() const
|
||||
{
|
||||
int tile_number = get_tile_number();
|
||||
return (tile_number - 1001) % 10;
|
||||
}
|
||||
|
||||
int get_tile_y_offset() const
|
||||
{
|
||||
int tile_number = get_tile_number();
|
||||
return (tile_number - 1001) / 10;
|
||||
}
|
||||
};
|
@@ -7,7 +7,12 @@
|
||||
#define IMAGE_DRAW_FLAG_DEPTH (1 << 3)
|
||||
#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4)
|
||||
|
||||
#ifdef TILED_IMAGE
|
||||
uniform sampler2DArray imageTileArray;
|
||||
uniform sampler1DArray imageTileData;
|
||||
#else
|
||||
uniform sampler2D imageTexture;
|
||||
#endif
|
||||
|
||||
uniform bool imgPremultiplied;
|
||||
uniform int drawFlags;
|
||||
@@ -15,50 +20,75 @@ uniform vec2 farNearDistances;
|
||||
uniform vec4 color;
|
||||
uniform vec4 shuffle;
|
||||
|
||||
/* Maximum UV range.
|
||||
* Negative UV coordinates and UV coordinates beyond maxUV would draw a border. */
|
||||
uniform vec2 maxUv;
|
||||
|
||||
#define FAR_DISTANCE farNearDistances.x
|
||||
#define NEAR_DISTANCE farNearDistances.y
|
||||
|
||||
#define Z_DEPTH_BORDER 1.0
|
||||
#define Z_DEPTH_IMAGE 0.75
|
||||
|
||||
in vec2 uv_screen;
|
||||
in vec2 uv_image;
|
||||
in vec2 uvs;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
bool is_border(vec2 uv)
|
||||
#ifdef TILED_IMAGE
|
||||
/* TODO(fclem): deduplicate code. */
|
||||
bool node_tex_tile_lookup(inout vec3 co, sampler2DArray ima, sampler1DArray map)
|
||||
{
|
||||
// TODO: should use bvec to reduce branching?
|
||||
return (uv.x < 0.0 || uv.y < 0.0 || uv.x > maxUv.x || uv.y > maxUv.y);
|
||||
vec2 tile_pos = floor(co.xy);
|
||||
|
||||
if (tile_pos.x < 0 || tile_pos.y < 0 || tile_pos.x >= 10) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float tile = 10.0 * tile_pos.y + tile_pos.x;
|
||||
if (tile >= textureSize(map, 0).x) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fetch tile information. */
|
||||
float tile_layer = texelFetch(map, ivec2(tile, 0), 0).x;
|
||||
if (tile_layer < 0.0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vec4 tile_info = texelFetch(map, ivec2(tile, 1), 0);
|
||||
|
||||
co = vec3(((co.xy - tile_pos) * tile_info.zw) + tile_info.xy, tile_layer);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 uvs_clamped = ivec2(uv_screen);
|
||||
vec4 tex_color = texelFetch(imageTexture, uvs_clamped, 0);
|
||||
vec4 tex_color;
|
||||
/* Read texture */
|
||||
#ifdef TILED_IMAGE
|
||||
vec3 co = vec3(uvs, 0.0);
|
||||
if (node_tex_tile_lookup(co, imageTileArray, imageTileData)) {
|
||||
tex_color = texture(imageTileArray, co);
|
||||
}
|
||||
else {
|
||||
tex_color = vec4(1.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
#else
|
||||
vec2 uvs_clamped = ((drawFlags & IMAGE_DRAW_FLAG_DO_REPEAT) != 0) ?
|
||||
fract(uvs) :
|
||||
clamp(uvs, vec2(0.0), vec2(1.0));
|
||||
tex_color = texture(imageTexture, uvs_clamped);
|
||||
#endif
|
||||
|
||||
bool border = is_border(uv_image);
|
||||
if (!border) {
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_APPLY_ALPHA) != 0) {
|
||||
if (!imgPremultiplied) {
|
||||
tex_color.rgb *= tex_color.a;
|
||||
}
|
||||
}
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_DEPTH) != 0) {
|
||||
tex_color = smoothstep(FAR_DISTANCE, NEAR_DISTANCE, tex_color);
|
||||
}
|
||||
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_SHUFFLING) != 0) {
|
||||
tex_color = color * dot(tex_color, shuffle);
|
||||
}
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_SHOW_ALPHA) == 0) {
|
||||
tex_color.a = 1.0;
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_APPLY_ALPHA) != 0) {
|
||||
if (!imgPremultiplied) {
|
||||
tex_color.rgb *= tex_color.a;
|
||||
}
|
||||
}
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_DEPTH) != 0) {
|
||||
tex_color = smoothstep(FAR_DISTANCE, NEAR_DISTANCE, tex_color);
|
||||
}
|
||||
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_SHUFFLING) != 0) {
|
||||
tex_color = color * dot(tex_color, shuffle);
|
||||
}
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_SHOW_ALPHA) == 0) {
|
||||
tex_color.a = 1.0;
|
||||
}
|
||||
|
||||
fragColor = tex_color;
|
||||
gl_FragDepth = border ? Z_DEPTH_BORDER : Z_DEPTH_IMAGE;
|
||||
}
|
||||
|
@@ -1,24 +1,34 @@
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
|
||||
#define IMAGE_DRAW_FLAG_DO_REPEAT (1 << 4)
|
||||
#define IMAGE_DRAW_FLAG_USE_WORLD_POS (1 << 5)
|
||||
#define IMAGE_Z_DEPTH 0.75
|
||||
|
||||
uniform int drawFlags;
|
||||
|
||||
in vec2 pos;
|
||||
in vec2 uv;
|
||||
|
||||
/* Normalized screen space uv coordinates. */
|
||||
out vec2 uv_screen;
|
||||
out vec2 uv_image;
|
||||
in vec3 pos;
|
||||
out vec2 uvs;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 image_pos = vec3(pos, 0.0);
|
||||
uv_screen = image_pos.xy;
|
||||
uv_image = uv;
|
||||
/* `pos` contains the coordinates of a quad (-1..1). but we need the coordinates of an image
|
||||
* plane (0..1) */
|
||||
vec3 image_pos = pos * 0.5 + 0.5;
|
||||
|
||||
vec3 world_pos = point_object_to_world(image_pos);
|
||||
vec4 position = point_world_to_ndc(world_pos);
|
||||
gl_Position = position;
|
||||
if ((drawFlags & IMAGE_DRAW_FLAG_DO_REPEAT) != 0) {
|
||||
gl_Position = vec4(pos.xy, IMAGE_Z_DEPTH, 1.0);
|
||||
uvs = point_view_to_object(image_pos).xy;
|
||||
}
|
||||
else {
|
||||
vec3 world_pos = point_object_to_world(image_pos);
|
||||
vec4 position = point_world_to_ndc(world_pos);
|
||||
/* Move drawn pixels to the front. In the overlay engine the depth is used
|
||||
* to detect if a transparency texture or the background color should be drawn.
|
||||
* Vertices are between 0.0 and 0.2, Edges between 0.2 and 0.4
|
||||
* actual pixels are at 0.75, 1.0 is used for the background. */
|
||||
position.z = IMAGE_Z_DEPTH;
|
||||
gl_Position = position;
|
||||
/* UDIM texture uses the world position for tile selection. */
|
||||
uvs = ((drawFlags & IMAGE_DRAW_FLAG_USE_WORLD_POS) != 0) ? world_pos.xy : image_pos.xy;
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ GPU_SHADER_CREATE_INFO(workbench_composite)
|
||||
.sampler(0, ImageType::FLOAT_2D, "normalBuffer", Frequency::PASS)
|
||||
.sampler(1, ImageType::FLOAT_2D, "materialBuffer", Frequency::PASS)
|
||||
.uniform_buf(4, "WorldData", "world_data", Frequency::PASS)
|
||||
.push_constant(Type::BOOL, "forceShadowing")
|
||||
.push_constant(0, Type::BOOL, "forceShadowing")
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.typedef_source("workbench_shader_shared.h")
|
||||
.fragment_source("workbench_composite_frag.glsl")
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
GPU_SHADER_CREATE_INFO(workbench_taa)
|
||||
.sampler(0, ImageType::FLOAT_2D, "colorBuffer")
|
||||
.push_constant(Type::FLOAT, "samplesWeights", 9)
|
||||
.push_constant(0, Type::FLOAT, "samplesWeights", 9)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.fragment_source("workbench_effect_taa_frag.glsl")
|
||||
.additional_info("draw_fullscreen")
|
||||
@@ -31,7 +31,7 @@ GPU_SHADER_CREATE_INFO(workbench_smaa)
|
||||
.define("SMAA_LUMA_WEIGHT", "float4(1.0, 1.0, 1.0, 1.0)")
|
||||
.define("SMAA_NO_DISCARD")
|
||||
.vertex_out(workbench_smaa_iface)
|
||||
.push_constant(Type::VEC4, "viewportMetrics")
|
||||
.push_constant(1, Type::VEC4, "viewportMetrics")
|
||||
.vertex_source("workbench_effect_smaa_vert.glsl")
|
||||
.fragment_source("workbench_effect_smaa_frag.glsl");
|
||||
|
||||
@@ -55,8 +55,8 @@ GPU_SHADER_CREATE_INFO(workbench_smaa_stage_2)
|
||||
.define("SMAA_STAGE", "2")
|
||||
.sampler(0, ImageType::FLOAT_2D, "colorTex")
|
||||
.sampler(1, ImageType::FLOAT_2D, "blendTex")
|
||||
.push_constant(Type::FLOAT, "mixFactor")
|
||||
.push_constant(Type::FLOAT, "taaAccumulatedWeight")
|
||||
.push_constant(2, Type::FLOAT, "mixFactor")
|
||||
.push_constant(3, Type::FLOAT, "taaAccumulatedWeight")
|
||||
.fragment_out(0, Type::VEC4, "out_color")
|
||||
.additional_info("workbench_smaa")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -11,10 +11,10 @@ GPU_SHADER_CREATE_INFO(workbench_effect_dof)
|
||||
.sampler(5, ImageType::FLOAT_2D, "halfResColorTex")
|
||||
.sampler(6, ImageType::FLOAT_2D, "blurTex")
|
||||
.sampler(7, ImageType::FLOAT_2D, "noiseTex")
|
||||
.push_constant(Type::VEC2, "invertedViewportSize")
|
||||
.push_constant(Type::VEC2, "nearFar")
|
||||
.push_constant(Type::VEC3, "dofParams")
|
||||
.push_constant(Type::FLOAT, "noiseOffset")
|
||||
.push_constant(0, Type::VEC2, "invertedViewportSize")
|
||||
.push_constant(1, Type::VEC2, "nearFar")
|
||||
.push_constant(2, Type::VEC3, "dofParams")
|
||||
.push_constant(3, Type::FLOAT, "noiseOffset")
|
||||
.fragment_source("workbench_effect_dof_frag.glsl")
|
||||
.additional_info("draw_fullscreen")
|
||||
.additional_info("draw_view");
|
||||
|
@@ -36,15 +36,15 @@ GPU_SHADER_CREATE_INFO(workbench_texture_none).define("TEXTURE_NONE");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(workbench_texture_single)
|
||||
.sampler(2, ImageType::FLOAT_2D, "imageTexture", Frequency::BATCH)
|
||||
.push_constant(Type::BOOL, "imagePremult")
|
||||
.push_constant(Type::FLOAT, "imageTransparencyCutoff")
|
||||
.push_constant(1, Type::BOOL, "imagePremult")
|
||||
.push_constant(2, Type::FLOAT, "imageTransparencyCutoff")
|
||||
.define("V3D_SHADING_TEXTURE_COLOR");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(workbench_texture_tile)
|
||||
.sampler(2, ImageType::FLOAT_2D_ARRAY, "imageTileArray", Frequency::BATCH)
|
||||
.sampler(3, ImageType::FLOAT_1D_ARRAY, "imageTileData", Frequency::BATCH)
|
||||
.push_constant(Type::BOOL, "imagePremult")
|
||||
.push_constant(Type::FLOAT, "imageTransparencyCutoff")
|
||||
.push_constant(1, Type::BOOL, "imagePremult")
|
||||
.push_constant(2, Type::FLOAT, "imageTransparencyCutoff")
|
||||
.define("V3D_SHADING_TEXTURE_COLOR")
|
||||
.define("TEXTURE_IMAGE_ARRAY");
|
||||
|
||||
@@ -79,8 +79,8 @@ GPU_SHADER_INTERFACE_INFO(workbench_material_iface, "")
|
||||
GPU_SHADER_CREATE_INFO(workbench_material)
|
||||
.uniform_buf(4, "WorldData", "world_data", Frequency::PASS)
|
||||
.uniform_buf(5, "vec4", "materials_data[4096]", Frequency::PASS)
|
||||
.push_constant(Type::INT, "materialIndex")
|
||||
.push_constant(Type::BOOL, "useMatcap")
|
||||
.push_constant(4, Type::INT, "materialIndex")
|
||||
.push_constant(5, Type::BOOL, "useMatcap")
|
||||
.vertex_out(workbench_material_iface);
|
||||
|
||||
/** \} */
|
||||
@@ -95,7 +95,7 @@ GPU_SHADER_CREATE_INFO(workbench_transparent_accum)
|
||||
.fragment_out(0, Type::VEC4, "transparentAccum")
|
||||
.fragment_out(1, Type::VEC4, "revealageAccum")
|
||||
.fragment_out(2, Type::UINT, "objectId")
|
||||
.push_constant(Type::BOOL, "forceShadowing")
|
||||
.push_constant(3, Type::BOOL, "forceShadowing")
|
||||
.typedef_source("workbench_shader_shared.h")
|
||||
.fragment_source("workbench_transparent_accum_frag.glsl");
|
||||
|
||||
|
@@ -13,8 +13,8 @@ GPU_SHADER_INTERFACE_INFO(workbench_shadow_iface, "vData")
|
||||
GPU_SHADER_CREATE_INFO(workbench_shadow_common)
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.vertex_out(workbench_shadow_iface)
|
||||
.push_constant(Type::FLOAT, "lightDistance")
|
||||
.push_constant(Type::VEC3, "lightDirection")
|
||||
.push_constant(0, Type::FLOAT, "lightDistance")
|
||||
.push_constant(1, Type::VEC3, "lightDirection")
|
||||
.vertex_source("workbench_shadow_vert.glsl")
|
||||
.additional_info("draw_mesh");
|
||||
|
||||
|
@@ -10,10 +10,10 @@ GPU_SHADER_CREATE_INFO(workbench_volume)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.sampler(0, ImageType::DEPTH_2D, "depthBuffer")
|
||||
.sampler(1, ImageType::FLOAT_3D, "densityTexture")
|
||||
.push_constant(Type::INT, "samplesLen")
|
||||
.push_constant(Type::FLOAT, "noiseOfs")
|
||||
.push_constant(Type::FLOAT, "stepLength")
|
||||
.push_constant(Type::FLOAT, "densityScale")
|
||||
.push_constant(28, Type::INT, "samplesLen")
|
||||
.push_constant(29, Type::FLOAT, "noiseOfs")
|
||||
.push_constant(30, Type::FLOAT, "stepLength")
|
||||
.push_constant(31, Type::FLOAT, "densityScale")
|
||||
.vertex_source("workbench_volume_vert.glsl")
|
||||
.fragment_source("workbench_volume_frag.glsl")
|
||||
.additional_info("draw_object_infos");
|
||||
@@ -32,9 +32,9 @@ GPU_SHADER_CREATE_INFO(workbench_volume_smoke)
|
||||
|
||||
GPU_SHADER_CREATE_INFO(workbench_volume_object)
|
||||
.define("VOLUME_OBJECT")
|
||||
.push_constant(Type::MAT4, "volumeTextureToObject")
|
||||
.push_constant(0, Type::MAT4, "volumeTextureToObject")
|
||||
/* FIXME(fclem): This overflow the push_constant limit. */
|
||||
.push_constant(Type::MAT4, "volumeObjectToTexture")
|
||||
.push_constant(16, Type::MAT4, "volumeObjectToTexture")
|
||||
.additional_info("draw_volume", "draw_resource_id_varying");
|
||||
|
||||
/** \} */
|
||||
@@ -47,15 +47,15 @@ GPU_SHADER_CREATE_INFO(workbench_volume_coba)
|
||||
.define("USE_COBA")
|
||||
.sampler(4, ImageType::UINT_3D, "flagTexture")
|
||||
.sampler(5, ImageType::FLOAT_1D, "transferTexture")
|
||||
.push_constant(Type::BOOL, "showPhi")
|
||||
.push_constant(Type::BOOL, "showFlags")
|
||||
.push_constant(Type::BOOL, "showPressure")
|
||||
.push_constant(Type::FLOAT, "gridScale");
|
||||
.push_constant(18, Type::BOOL, "showPhi")
|
||||
.push_constant(19, Type::BOOL, "showFlags")
|
||||
.push_constant(20, Type::BOOL, "showPressure")
|
||||
.push_constant(21, Type::FLOAT, "gridScale");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(workbench_volume_no_coba)
|
||||
.sampler(4, ImageType::FLOAT_3D, "shadowTexture")
|
||||
.sampler(5, ImageType::UINT_2D, "transferTexture")
|
||||
.push_constant(Type::VEC3, "activeColor");
|
||||
.push_constant(18, Type::VEC3, "activeColor");
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -79,8 +79,8 @@ GPU_SHADER_CREATE_INFO(workbench_volume_slice)
|
||||
.define("VOLUME_SLICE")
|
||||
.vertex_in(1, Type::VEC3, "uvs")
|
||||
.vertex_out(workbench_volume_iface)
|
||||
.push_constant(Type::INT, "sliceAxis") /* -1 is no slice. */
|
||||
.push_constant(Type::FLOAT, "slicePosition");
|
||||
.push_constant(32, Type::INT, "sliceAxis") /* -1 is no slice. */
|
||||
.push_constant(33, Type::FLOAT, "slicePosition");
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -64,7 +64,7 @@ static struct {
|
||||
|
||||
struct GPUShader *volume_sh[2][2][3][2];
|
||||
|
||||
} e_data = {{{{nullptr}}}};
|
||||
} e_data = {{{{NULL}}}};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Conversions
|
||||
|
@@ -72,7 +72,10 @@
|
||||
|
||||
#include "draw_texture_pool.h"
|
||||
|
||||
#include "BLI_math_vec_types.hh"
|
||||
#include "BLI_float4.hh"
|
||||
#include "BLI_int2.hh"
|
||||
#include "BLI_int3.hh"
|
||||
#include "BLI_int4.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
@@ -102,8 +105,7 @@ class DataBuffer {
|
||||
T *data_ = nullptr;
|
||||
int64_t len_ = len;
|
||||
|
||||
BLI_STATIC_ASSERT(((sizeof(T) * len) % 16) == 0,
|
||||
"Buffer size need to be aligned to size of float4.");
|
||||
BLI_STATIC_ASSERT((sizeof(T) % 16) == 0, "Type need to be aligned to size of float4.");
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -293,7 +295,7 @@ class UniformArrayBuffer : public detail::UniformCommon<T, len, false> {
|
||||
UniformArrayBuffer()
|
||||
{
|
||||
/* TODO(fclem) We should map memory instead. */
|
||||
this->data_ = (T *)MEM_mallocN_aligned(len * sizeof(T), 16, this->name_);
|
||||
this->data_ = MEM_mallocN_aligned(this->name_);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -482,7 +484,7 @@ class Texture : NonCopyable {
|
||||
* Ensure the texture has the correct properties. Recreating it if needed.
|
||||
* Return true if a texture has been created.
|
||||
*/
|
||||
bool ensure_2d(eGPUTextureFormat format, int2 extent, float *data = nullptr, int mips = 1)
|
||||
bool ensure_2d(eGPUTextureFormat format, const int2 &extent, float *data = nullptr, int mips = 1)
|
||||
{
|
||||
return ensure_impl(UNPACK2(extent), 0, mips, format, data, false, false);
|
||||
}
|
||||
@@ -491,8 +493,11 @@ class Texture : NonCopyable {
|
||||
* Ensure the texture has the correct properties. Recreating it if needed.
|
||||
* Return true if a texture has been created.
|
||||
*/
|
||||
bool ensure_2d_array(
|
||||
eGPUTextureFormat format, int2 extent, int layers, float *data = nullptr, int mips = 1)
|
||||
bool ensure_2d_array(eGPUTextureFormat format,
|
||||
const int2 &extent,
|
||||
int layers,
|
||||
float *data = nullptr,
|
||||
int mips = 1)
|
||||
{
|
||||
return ensure_impl(UNPACK2(extent), layers, mips, format, data, true, false);
|
||||
}
|
||||
@@ -501,7 +506,7 @@ class Texture : NonCopyable {
|
||||
* Ensure the texture has the correct properties. Recreating it if needed.
|
||||
* Return true if a texture has been created.
|
||||
*/
|
||||
bool ensure_3d(eGPUTextureFormat format, int3 extent, float *data = nullptr, int mips = 1)
|
||||
bool ensure_3d(eGPUTextureFormat format, const int3 &extent, float *data = nullptr, int mips = 1)
|
||||
{
|
||||
return ensure_impl(UNPACK3(extent), mips, format, data, false, false);
|
||||
}
|
||||
@@ -591,6 +596,14 @@ class Texture : NonCopyable {
|
||||
GPU_texture_clear(tx_, GPU_DATA_UINT, &values[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the entirety of the texture using one pixel worth of data.
|
||||
*/
|
||||
void clear(uchar4 values)
|
||||
{
|
||||
GPU_texture_clear(tx_, GPU_DATA_UBYTE, &values[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the entirety of the texture using one pixel worth of data.
|
||||
*/
|
||||
@@ -621,15 +634,6 @@ class Texture : NonCopyable {
|
||||
GPU_TEXTURE_FREE_SAFE(tx_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the content of the two textures.
|
||||
*/
|
||||
static void swap(Texture &a, Texture &b)
|
||||
{
|
||||
SWAP(GPUTexture *, a.tx_, b.tx_);
|
||||
SWAP(const char *, a.name_, b.name_);
|
||||
}
|
||||
|
||||
private:
|
||||
bool ensure_impl(int w,
|
||||
int h = 0,
|
||||
@@ -709,7 +713,7 @@ class TextureFromPool : public Texture, NonMovable {
|
||||
TextureFromPool(const char *name = "gpu::Texture") : Texture(name){};
|
||||
|
||||
/* Always use `release()` after rendering. */
|
||||
void acquire(int2 extent, eGPUTextureFormat format, void *owner_)
|
||||
void acquire(int w, int h, eGPUTextureFormat format, void *owner_)
|
||||
{
|
||||
if (this->tx_ == nullptr) {
|
||||
if (tx_tmp_saved_ != nullptr) {
|
||||
@@ -717,7 +721,7 @@ class TextureFromPool : public Texture, NonMovable {
|
||||
return;
|
||||
}
|
||||
DrawEngineType *owner = (DrawEngineType *)owner_;
|
||||
this->tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), format, owner);
|
||||
this->tx_ = DRW_texture_pool_query_2d(w, h, format, owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,6 +750,11 @@ class TextureFromPool : public Texture, NonMovable {
|
||||
bool ensure_cube_array(int, int, int, eGPUTextureFormat, float *) = delete;
|
||||
void filter_mode(bool) = delete;
|
||||
void free() = delete;
|
||||
/**
|
||||
* Forbid the use of DRW_shgroup_uniform_texture.
|
||||
* Use DRW_shgroup_uniform_texture_ref instead.
|
||||
*/
|
||||
operator GPUTexture *() const = delete;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
@@ -796,15 +805,6 @@ class Framebuffer : NonCopyable {
|
||||
{
|
||||
return fb_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the content of the two framebuffer.
|
||||
*/
|
||||
static void swap(Framebuffer &a, Framebuffer &b)
|
||||
{
|
||||
SWAP(GPUFrameBuffer *, a.fb_, b.fb_);
|
||||
SWAP(const char *, a.name_, b.name_);
|
||||
}
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
@@ -293,9 +293,7 @@ DRWShaderLibrary *DRW_shader_library_create(void);
|
||||
/**
|
||||
* \warning Each library must be added after all its dependencies.
|
||||
*/
|
||||
void DRW_shader_library_add_file(DRWShaderLibrary *lib,
|
||||
const char *lib_code,
|
||||
const char *lib_name);
|
||||
void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name);
|
||||
#define DRW_SHADER_LIB_ADD(lib, lib_name) \
|
||||
DRW_shader_library_add_file(lib, datatoc_##lib_name##_glsl, STRINGIFY(lib_name) ".glsl")
|
||||
|
||||
@@ -698,7 +696,7 @@ const DRWView *DRW_view_default_get(void);
|
||||
/**
|
||||
* MUST only be called once per render and only in render mode. Sets default view.
|
||||
*/
|
||||
void DRW_view_default_set(const DRWView *view);
|
||||
void DRW_view_default_set(DRWView *view);
|
||||
/**
|
||||
* \warning Only use in render AND only if you are going to set view_default again.
|
||||
*/
|
||||
@@ -706,7 +704,7 @@ void DRW_view_reset(void);
|
||||
/**
|
||||
* Set active view for rendering.
|
||||
*/
|
||||
void DRW_view_set_active(const DRWView *view);
|
||||
void DRW_view_set_active(DRWView *view);
|
||||
const DRWView *DRW_view_get_active(void);
|
||||
|
||||
/**
|
||||
|
@@ -1904,10 +1904,10 @@ void DRW_view_reset(void)
|
||||
DST.view_previous = NULL;
|
||||
}
|
||||
|
||||
void DRW_view_default_set(const DRWView *view)
|
||||
void DRW_view_default_set(DRWView *view)
|
||||
{
|
||||
BLI_assert(DST.view_default == NULL);
|
||||
DST.view_default = (DRWView *)view;
|
||||
DST.view_default = view;
|
||||
}
|
||||
|
||||
void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len)
|
||||
|
@@ -354,9 +354,9 @@ static bool draw_call_is_culled(const DRWResourceHandle *handle, DRWView *view)
|
||||
return (culling->mask & view->culling_mask) != 0;
|
||||
}
|
||||
|
||||
void DRW_view_set_active(const DRWView *view)
|
||||
void DRW_view_set_active(DRWView *view)
|
||||
{
|
||||
DST.view_active = (view != NULL) ? ((DRWView *)view) : DST.view_default;
|
||||
DST.view_active = (view) ? view : DST.view_default;
|
||||
}
|
||||
|
||||
const DRWView *DRW_view_get_active(void)
|
||||
|
@@ -567,7 +567,7 @@ void DRW_shader_free(GPUShader *shader)
|
||||
#define MAX_LIB_DEPS 8
|
||||
|
||||
struct DRWShaderLibrary {
|
||||
const char *libs[MAX_LIB];
|
||||
char *libs[MAX_LIB];
|
||||
char libs_name[MAX_LIB][MAX_LIB_NAME];
|
||||
uint32_t libs_deps[MAX_LIB];
|
||||
};
|
||||
@@ -629,7 +629,7 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c
|
||||
return deps;
|
||||
}
|
||||
|
||||
void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, const char *lib_name)
|
||||
void DRW_shader_library_add_file(DRWShaderLibrary *lib, char *lib_code, const char *lib_name)
|
||||
{
|
||||
int index = -1;
|
||||
for (int i = 0; i < MAX_LIB; i++) {
|
||||
|
@@ -29,14 +29,14 @@ GPU_SHADER_CREATE_INFO(draw_hair_refine_compute)
|
||||
.sampler(0, ImageType::FLOAT_BUFFER, "hairPointBuffer")
|
||||
.sampler(1, ImageType::UINT_BUFFER, "hairStrandBuffer")
|
||||
.sampler(2, ImageType::UINT_BUFFER, "hairStrandSegBuffer")
|
||||
.push_constant(Type::VEC4, "hairDupliMatrix", 4)
|
||||
.push_constant(Type::BOOL, "hairCloseTip")
|
||||
.push_constant(Type::FLOAT, "hairRadShape")
|
||||
.push_constant(Type::FLOAT, "hairRadTip")
|
||||
.push_constant(Type::FLOAT, "hairRadRoot")
|
||||
.push_constant(Type::INT, "hairThicknessRes")
|
||||
.push_constant(Type::INT, "hairStrandsRes")
|
||||
.push_constant(Type::INT, "hairStrandOffset")
|
||||
.push_constant(0, Type::VEC4, "hairDupliMatrix", 4)
|
||||
.push_constant(16, Type::BOOL, "hairCloseTip")
|
||||
.push_constant(17, Type::FLOAT, "hairRadShape")
|
||||
.push_constant(18, Type::FLOAT, "hairRadTip")
|
||||
.push_constant(19, Type::FLOAT, "hairRadRoot")
|
||||
.push_constant(20, Type::INT, "hairThicknessRes")
|
||||
.push_constant(21, Type::INT, "hairStrandsRes")
|
||||
.push_constant(22, Type::INT, "hairStrandOffset")
|
||||
.compute_source("common_hair_refine_comp.glsl")
|
||||
.define("HAIR_PHASE_SUBDIV")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -26,7 +26,7 @@ GPU_SHADER_CREATE_INFO(draw_resource_id_varying)
|
||||
/* Variation used when drawing multiple instances for one object. */
|
||||
GPU_SHADER_CREATE_INFO(draw_resource_id_uniform)
|
||||
.define("UNIFORM_RESOURCE_ID")
|
||||
.push_constant(Type::INT, "drw_ResourceID");
|
||||
.push_constant(64, Type::INT, "drw_ResourceID");
|
||||
|
||||
/**
|
||||
* Declare a resource handle that identify a unique object.
|
||||
@@ -34,7 +34,7 @@ GPU_SHADER_CREATE_INFO(draw_resource_id_uniform)
|
||||
*/
|
||||
GPU_SHADER_CREATE_INFO(draw_resource_handle)
|
||||
.define("resource_handle (drw_resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id)")
|
||||
.push_constant(Type::INT, "drw_resourceChunk");
|
||||
.push_constant(63, Type::INT, "drw_resourceChunk");
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -54,13 +54,13 @@ GPU_SHADER_CREATE_INFO(draw_modelmat)
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_modelmat_legacy)
|
||||
.define("DRW_LEGACY_MODEL_MATRIX")
|
||||
.push_constant(Type::MAT4, "ModelMatrix")
|
||||
.push_constant(Type::MAT4, "ModelMatrixInverse")
|
||||
.push_constant(38, Type::MAT4, "ModelMatrix")
|
||||
.push_constant(54, Type::MAT4, "ModelMatrixInverse")
|
||||
.additional_info("draw_view");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_modelmat_instanced_attr)
|
||||
.push_constant(Type::MAT4, "ModelMatrix")
|
||||
.push_constant(Type::MAT4, "ModelMatrixInverse")
|
||||
.push_constant(0, Type::MAT4, "ModelMatrix")
|
||||
.push_constant(16, Type::MAT4, "ModelMatrixInverse")
|
||||
.additional_info("draw_view");
|
||||
|
||||
/** \} */
|
||||
@@ -84,14 +84,14 @@ GPU_SHADER_CREATE_INFO(draw_hair)
|
||||
.sampler(14, ImageType::UINT_BUFFER, "hairStrandBuffer")
|
||||
.sampler(13, ImageType::UINT_BUFFER, "hairStrandSegBuffer")
|
||||
/* TODO(fclem) Pack thoses into one UBO. */
|
||||
.push_constant(Type::INT, "hairStrandsRes")
|
||||
.push_constant(Type::INT, "hairThicknessRes")
|
||||
.push_constant(Type::FLOAT, "hairRadRoot")
|
||||
.push_constant(Type::FLOAT, "hairRadTip")
|
||||
.push_constant(Type::FLOAT, "hairRadShape")
|
||||
.push_constant(Type::BOOL, "hairCloseTip")
|
||||
.push_constant(Type::INT, "hairStrandOffset")
|
||||
.push_constant(Type::VEC4, "hairDupliMatrix", 4)
|
||||
.push_constant(9, Type::INT, "hairStrandsRes")
|
||||
.push_constant(10, Type::INT, "hairThicknessRes")
|
||||
.push_constant(11, Type::FLOAT, "hairRadRoot")
|
||||
.push_constant(12, Type::FLOAT, "hairRadTip")
|
||||
.push_constant(13, Type::FLOAT, "hairRadShape")
|
||||
.push_constant(14, Type::BOOL, "hairCloseTip")
|
||||
.push_constant(15, Type::INT, "hairStrandOffset")
|
||||
.push_constant(16, Type::VEC4, "hairDupliMatrix", 4)
|
||||
.additional_info("draw_modelmat", "draw_resource_id");
|
||||
|
||||
GPU_SHADER_CREATE_INFO(draw_pointcloud)
|
||||
|
@@ -809,18 +809,12 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
|
||||
else {
|
||||
if (is_array_component) {
|
||||
ot = WM_operatortype_find("UI_OT_override_type_set_button", false);
|
||||
uiItemFullO_ptr(layout,
|
||||
ot,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Overrides"),
|
||||
ICON_NONE,
|
||||
NULL,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
0,
|
||||
&op_ptr);
|
||||
uiItemFullO_ptr(
|
||||
layout, ot, "Define Overrides", ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0, &op_ptr);
|
||||
RNA_boolean_set(&op_ptr, "all", true);
|
||||
uiItemFullO_ptr(layout,
|
||||
ot,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Single Override"),
|
||||
"Define Single Override",
|
||||
ICON_NONE,
|
||||
NULL,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
@@ -831,7 +825,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
|
||||
else {
|
||||
uiItemFullO(layout,
|
||||
"UI_OT_override_type_set_button",
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Define Override"),
|
||||
"Define Override",
|
||||
ICON_NONE,
|
||||
NULL,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
|
@@ -597,9 +597,6 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op)
|
||||
opop->operation = operation;
|
||||
}
|
||||
|
||||
/* Outliner e.g. has to be aware of this change. */
|
||||
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
|
||||
|
||||
return operator_button_property_finish(C, &ptr, prop);
|
||||
}
|
||||
|
||||
@@ -717,9 +714,6 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
}
|
||||
|
||||
/* Outliner e.g. has to be aware of this change. */
|
||||
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
|
||||
|
||||
return operator_button_property_finish(C, &ptr, prop);
|
||||
}
|
||||
|
||||
|
@@ -131,11 +131,6 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
||||
const bool use_instancing = RNA_boolean_get(op->ptr, "use_instancing");
|
||||
const bool evaluation_mode = RNA_enum_get(op->ptr, "evaluation_mode");
|
||||
|
||||
const bool generate_preview_surface = RNA_boolean_get(op->ptr, "generate_preview_surface");
|
||||
const bool export_textures = RNA_boolean_get(op->ptr, "export_textures");
|
||||
const bool overwrite_textures = RNA_boolean_get(op->ptr, "overwrite_textures");
|
||||
const bool relative_texture_paths = RNA_boolean_get(op->ptr, "relative_texture_paths");
|
||||
|
||||
struct USDExportParams params = {
|
||||
export_animation,
|
||||
export_hair,
|
||||
@@ -146,10 +141,6 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
||||
visible_objects_only,
|
||||
use_instancing,
|
||||
evaluation_mode,
|
||||
generate_preview_surface,
|
||||
export_textures,
|
||||
overwrite_textures,
|
||||
relative_texture_paths,
|
||||
};
|
||||
|
||||
bool ok = USD_export(C, filename, ¶ms, as_background_job);
|
||||
@@ -181,26 +172,6 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op)
|
||||
col = uiLayoutColumn(box, true);
|
||||
uiItemR(col, ptr, "evaluation_mode", 0, NULL, ICON_NONE);
|
||||
|
||||
box = uiLayoutBox(layout);
|
||||
col = uiLayoutColumnWithHeading(box, true, IFACE_("Materials"));
|
||||
uiItemR(col, ptr, "generate_preview_surface", 0, NULL, ICON_NONE);
|
||||
const bool export_mtl = RNA_boolean_get(ptr, "export_materials");
|
||||
uiLayoutSetActive(col, export_mtl);
|
||||
|
||||
uiLayout *row = uiLayoutRow(col, true);
|
||||
uiItemR(row, ptr, "export_textures", 0, NULL, ICON_NONE);
|
||||
const bool preview = RNA_boolean_get(ptr, "generate_preview_surface");
|
||||
uiLayoutSetActive(row, export_mtl && preview);
|
||||
|
||||
row = uiLayoutRow(col, true);
|
||||
uiItemR(row, ptr, "overwrite_textures", 0, NULL, ICON_NONE);
|
||||
const bool export_tex = RNA_boolean_get(ptr, "export_textures");
|
||||
uiLayoutSetActive(row, export_mtl && preview && export_tex);
|
||||
|
||||
row = uiLayoutRow(col, true);
|
||||
uiItemR(row, ptr, "relative_texture_paths", 0, NULL, ICON_NONE);
|
||||
uiLayoutSetActive(row, export_mtl && preview);
|
||||
|
||||
box = uiLayoutBox(layout);
|
||||
uiItemL(box, IFACE_("Experimental"), ICON_NONE);
|
||||
uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE);
|
||||
@@ -278,32 +249,6 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
|
||||
"Use Settings for",
|
||||
"Determines visibility of objects, modifier settings, and other areas where there "
|
||||
"are different settings for viewport and rendering");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"generate_preview_surface",
|
||||
true,
|
||||
"To USD Preview Surface",
|
||||
"Generate an approximate USD Preview Surface shader "
|
||||
"representation of a Principled BSDF node network");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"export_textures",
|
||||
true,
|
||||
"Export Textures",
|
||||
"If exporting materials, export textures referenced by material nodes "
|
||||
"to a 'textures' directory in the same directory as the USD file");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"overwrite_textures",
|
||||
false,
|
||||
"Overwrite Textures",
|
||||
"Allow overwriting existing texture files when exporting textures");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"relative_texture_paths",
|
||||
true,
|
||||
"Relative Texture Paths",
|
||||
"Make texture asset paths relative to the USD file");
|
||||
}
|
||||
|
||||
/* ====== USD Import ====== */
|
||||
|
@@ -603,20 +603,6 @@ static bool data_transfer_poll_property(const bContext *UNUSED(C),
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *data_transfer_get_description(bContext *UNUSED(C),
|
||||
wmOperatorType *UNUSED(ot),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
const bool reverse_transfer = RNA_boolean_get(ptr, "use_reverse_transfer");
|
||||
|
||||
if (reverse_transfer) {
|
||||
return BLI_strdup(
|
||||
"Transfer data layer(s) (weights, edge sharp, etc.) from selected meshes to active one");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void OBJECT_OT_data_transfer(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
@@ -633,7 +619,6 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot)
|
||||
ot->invoke = WM_menu_invoke;
|
||||
ot->exec = data_transfer_exec;
|
||||
ot->check = data_transfer_check;
|
||||
ot->get_description = data_transfer_get_description;
|
||||
|
||||
/* Flags. */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
@@ -1976,14 +1976,8 @@ static void move_to_collection_menu_create(bContext *C, uiLayout *layout, void *
|
||||
RNA_int_set(&menu->ptr, "collection_index", menu->index);
|
||||
RNA_boolean_set(&menu->ptr, "is_new", true);
|
||||
|
||||
uiItemFullO_ptr(layout,
|
||||
menu->ot,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "New Collection"),
|
||||
ICON_ADD,
|
||||
menu->ptr.data,
|
||||
WM_OP_INVOKE_DEFAULT,
|
||||
0,
|
||||
NULL);
|
||||
uiItemFullO_ptr(
|
||||
layout, menu->ot, "New Collection", ICON_ADD, menu->ptr.data, WM_OP_INVOKE_DEFAULT, 0, NULL);
|
||||
|
||||
uiItemS(layout);
|
||||
|
||||
|
@@ -616,14 +616,8 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
|
||||
ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL) {
|
||||
image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, &tile_rect, offset_x, offset_y, viewname);
|
||||
}
|
||||
ImageTile *image_tile = BKE_image_get_tile(ima, 0);
|
||||
BKE_image_update_gputexture_delayed(ima,
|
||||
image_tile,
|
||||
ibuf,
|
||||
offset_x,
|
||||
offset_y,
|
||||
BLI_rcti_size_x(&tile_rect),
|
||||
BLI_rcti_size_y(&tile_rect));
|
||||
BKE_image_update_gputexture_delayed(
|
||||
ima, ibuf, offset_x, offset_y, BLI_rcti_size_x(&tile_rect), BLI_rcti_size_y(&tile_rect));
|
||||
|
||||
/* make jobs timer to send notifier */
|
||||
*(rj->do_update) = true;
|
||||
|
@@ -1611,7 +1611,8 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
|
||||
|
||||
if (!(te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]))) {
|
||||
if (deselect_all) {
|
||||
changed |= outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false);
|
||||
outliner_flag_set(&space_outliner->tree, TSE_SELECTED, false);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
/* Don't allow toggle on scene collection */
|
||||
@@ -1659,18 +1660,16 @@ static int outliner_item_do_activate_from_cursor(bContext *C,
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (changed) {
|
||||
if (rebuild_tree) {
|
||||
ED_region_tag_redraw(region);
|
||||
}
|
||||
else {
|
||||
ED_region_tag_redraw_no_rebuild(region);
|
||||
}
|
||||
|
||||
if (rebuild_tree) {
|
||||
ED_region_tag_redraw(region);
|
||||
ED_outliner_select_sync_from_outliner(C, space_outliner);
|
||||
}
|
||||
else {
|
||||
ED_region_tag_redraw_no_rebuild(region);
|
||||
}
|
||||
|
||||
ED_outliner_select_sync_from_outliner(C, space_outliner);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
@@ -272,7 +272,7 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl);
|
||||
* Fills the whole texture with the same data for all pixels.
|
||||
* \warning Only work for 2D texture for now.
|
||||
* \warning Only clears the mip 0 of the texture.
|
||||
* \param data_format: data format of the pixel data. \note The format is float for unorm textures.
|
||||
* \param data_format: data format of the pixel data.
|
||||
* \param data: 1 pixel worth of data to fill the texture with.
|
||||
*/
|
||||
void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data);
|
||||
|
@@ -34,7 +34,6 @@
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_shader_create_info_private.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
#undef GPU_SHADER_INTERFACE_INFO
|
||||
#undef GPU_SHADER_CREATE_INFO
|
||||
@@ -228,7 +227,6 @@ void gpu_shader_create_info_exit()
|
||||
|
||||
bool gpu_shader_create_info_compile_all()
|
||||
{
|
||||
using namespace blender::gpu;
|
||||
int success = 0;
|
||||
int total = 0;
|
||||
for (ShaderCreateInfo *info : g_create_infos->values()) {
|
||||
@@ -241,49 +239,6 @@ bool gpu_shader_create_info_compile_all()
|
||||
}
|
||||
else {
|
||||
success++;
|
||||
|
||||
#if 0 /* TODO(fclem): This is too verbose for now. Make it a cmake option. */
|
||||
/* Test if any resource is optimized out and print a warning if that's the case. */
|
||||
/* TODO(fclem): Limit this to OpenGL backend. */
|
||||
const ShaderInterface *interface = unwrap(shader)->interface;
|
||||
|
||||
blender::Vector<ShaderCreateInfo::Resource> all_resources;
|
||||
all_resources.extend(info->pass_resources_);
|
||||
all_resources.extend(info->batch_resources_);
|
||||
|
||||
for (ShaderCreateInfo::Resource &res : all_resources) {
|
||||
blender::StringRefNull name = "";
|
||||
const ShaderInput *input = nullptr;
|
||||
|
||||
switch (res.bind_type) {
|
||||
case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
|
||||
input = interface->ubo_get(res.slot);
|
||||
name = res.uniformbuf.name;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
|
||||
input = interface->ssbo_get(res.slot);
|
||||
name = res.storagebuf.name;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::SAMPLER:
|
||||
input = interface->texture_get(res.slot);
|
||||
name = res.sampler.name;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::IMAGE:
|
||||
input = interface->texture_get(res.slot);
|
||||
name = res.image.name;
|
||||
break;
|
||||
}
|
||||
|
||||
if (input == nullptr) {
|
||||
std::cout << "Error: " << info->name_;
|
||||
std::cout << ": Resource « " << name << " » not found in the shader interface\n";
|
||||
}
|
||||
else if (input->location == -1) {
|
||||
std::cout << "Warning: " << info->name_;
|
||||
std::cout << ": Resource « " << name << " » is optimized out\n";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
GPU_shader_free(shader);
|
||||
}
|
||||
|
@@ -314,6 +314,7 @@ struct ShaderCreateInfo {
|
||||
Vector<StageInterfaceInfo *> geometry_out_interfaces_;
|
||||
|
||||
struct PushConst {
|
||||
int index;
|
||||
Type type;
|
||||
StringRefNull name;
|
||||
int array_size;
|
||||
@@ -502,14 +503,22 @@ struct ShaderCreateInfo {
|
||||
/** \name Push constants
|
||||
*
|
||||
* Data managed by GPUShader. Can be set through uniform functions. Must be less than 128bytes.
|
||||
* One slot represents 4bytes. Each element needs to have enough empty space left after it.
|
||||
* example:
|
||||
* [0] = PUSH_CONSTANT(MAT4, "ModelMatrix"),
|
||||
* ---- 16 slots occupied by ModelMatrix ----
|
||||
* [16] = PUSH_CONSTANT(VEC4, "color"),
|
||||
* ---- 4 slots occupied by color ----
|
||||
* [20] = PUSH_CONSTANT(BOOL, "srgbToggle"),
|
||||
* The maximum slot is 31.
|
||||
* \{ */
|
||||
|
||||
Self &push_constant(Type type, StringRefNull name, int array_size = 0)
|
||||
Self &push_constant(int slot, Type type, StringRefNull name, int array_size = 0)
|
||||
{
|
||||
BLI_assert_msg(name.find("[") == -1,
|
||||
"Array syntax is forbidden for push constants."
|
||||
"Use the array_size parameter instead.");
|
||||
push_constants_.append({type, name, array_size});
|
||||
push_constants_.append({slot, type, name, array_size});
|
||||
interface_names_size_ += name.size() + 1;
|
||||
return *(Self *)this;
|
||||
}
|
||||
|
@@ -292,18 +292,17 @@ struct GPUSource {
|
||||
source = processed_source.c_str();
|
||||
};
|
||||
|
||||
/* Return 1 one error. */
|
||||
int init_dependencies(const GPUSourceDictionnary &dict)
|
||||
void init_dependencies(const GPUSourceDictionnary &dict)
|
||||
{
|
||||
if (dependencies_init) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
dependencies_init = true;
|
||||
int64_t pos = 0;
|
||||
while (true) {
|
||||
pos = source.find("pragma BLENDER_REQUIRE(", pos);
|
||||
if (pos == -1) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
int64_t start = source.find('(', pos) + 1;
|
||||
int64_t end = source.find(')', pos);
|
||||
@@ -311,7 +310,7 @@ struct GPUSource {
|
||||
/* TODO Use clog. */
|
||||
std::cout << "Error: " << filename << " : Malformed BLENDER_REQUIRE: Missing \")\"."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
StringRef dependency_name = source.substr(start, end - start);
|
||||
GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr);
|
||||
@@ -319,13 +318,10 @@ struct GPUSource {
|
||||
/* TODO Use clog. */
|
||||
std::cout << "Error: " << filename << " : Dependency not found \"" << dependency_name
|
||||
<< "\"." << std::endl;
|
||||
return 1;
|
||||
return;
|
||||
}
|
||||
/* Recursive. */
|
||||
int result = dependency_source->init_dependencies(dict);
|
||||
if (result != 0) {
|
||||
return 1;
|
||||
}
|
||||
dependency_source->init_dependencies(dict);
|
||||
|
||||
for (auto *dep : dependency_source->dependencies) {
|
||||
dependencies.append_non_duplicates(dep);
|
||||
@@ -362,11 +358,9 @@ void gpu_shader_dependency_init()
|
||||
#include "glsl_gpu_source_list.h"
|
||||
#undef SHADER_SOURCE
|
||||
|
||||
int errors = 0;
|
||||
for (auto *value : g_sources->values()) {
|
||||
errors += value->init_dependencies(*g_sources);
|
||||
value->init_dependencies(*g_sources);
|
||||
}
|
||||
BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting");
|
||||
}
|
||||
|
||||
void gpu_shader_dependency_exit()
|
||||
|
@@ -107,10 +107,6 @@ class ShaderInterface {
|
||||
{
|
||||
return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name);
|
||||
}
|
||||
inline const ShaderInput *ssbo_get(const int binding) const
|
||||
{
|
||||
return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, binding);
|
||||
}
|
||||
|
||||
inline const char *input_name_get(const ShaderInput *input) const
|
||||
{
|
||||
|
@@ -137,24 +137,25 @@ struct DRWData **GPU_viewport_data_get(GPUViewport *viewport)
|
||||
static void gpu_viewport_textures_create(GPUViewport *viewport)
|
||||
{
|
||||
int *size = viewport->size;
|
||||
float empty_pixel[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
float empty_pixel_fl[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
uchar empty_pixel_u[4] = {0, 0, 0, 0};
|
||||
|
||||
if (viewport->color_render_tx[0] == NULL) {
|
||||
viewport->color_render_tx[0] = GPU_texture_create_2d(
|
||||
"dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL);
|
||||
GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel);
|
||||
GPU_texture_clear(viewport->color_render_tx[0], GPU_DATA_FLOAT, empty_pixel_fl);
|
||||
viewport->color_overlay_tx[0] = GPU_texture_create_2d(
|
||||
"dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL);
|
||||
GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_FLOAT, empty_pixel);
|
||||
GPU_texture_clear(viewport->color_overlay_tx[0], GPU_DATA_UBYTE, empty_pixel_u);
|
||||
}
|
||||
|
||||
if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == NULL) {
|
||||
viewport->color_render_tx[1] = GPU_texture_create_2d(
|
||||
"dtxl_color_stereo", UNPACK2(size), 1, GPU_RGBA16F, NULL);
|
||||
GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel);
|
||||
GPU_texture_clear(viewport->color_render_tx[1], GPU_DATA_FLOAT, empty_pixel_fl);
|
||||
viewport->color_overlay_tx[1] = GPU_texture_create_2d(
|
||||
"dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, NULL);
|
||||
GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_FLOAT, empty_pixel);
|
||||
GPU_texture_clear(viewport->color_overlay_tx[1], GPU_DATA_UBYTE, empty_pixel_u);
|
||||
}
|
||||
|
||||
/* Can be shared with GPUOffscreen. */
|
||||
|
@@ -429,15 +429,8 @@ void GLFrameBuffer::read(eGPUFrameBufferBits plane,
|
||||
switch (plane) {
|
||||
case GPU_DEPTH_BIT:
|
||||
format = GL_DEPTH_COMPONENT;
|
||||
BLI_assert_msg(
|
||||
this->attachments_[GPU_FB_DEPTH_ATTACHMENT].tex != nullptr ||
|
||||
this->attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != nullptr,
|
||||
"GPUFramebuffer: Error: Trying to read depth without a depth buffer attached.");
|
||||
break;
|
||||
case GPU_COLOR_BIT:
|
||||
BLI_assert_msg(
|
||||
mode != GL_NONE,
|
||||
"GPUFramebuffer: Error: Trying to read a color slot without valid attachment.");
|
||||
format = channel_len_to_gl(channel_len);
|
||||
/* TODO: needed for selection buffers to work properly, this should be handled better. */
|
||||
if (format == GL_RED && type == GL_UNSIGNED_INT) {
|
||||
|
@@ -409,6 +409,9 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const
|
||||
}
|
||||
ss << "\n/* Push Constants. */\n";
|
||||
for (const ShaderCreateInfo::PushConst &uniform : info.push_constants_) {
|
||||
if (GLContext::explicit_location_support) {
|
||||
ss << "layout(location = " << uniform.index << ") ";
|
||||
}
|
||||
ss << "uniform " << to_string(uniform.type) << " " << uniform.name;
|
||||
if (uniform.array_size > 0) {
|
||||
ss << "[" << uniform.array_size << "]";
|
||||
|
@@ -437,7 +437,10 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
|
||||
}
|
||||
for (const ShaderCreateInfo::PushConst &uni : info.push_constants_) {
|
||||
copy_input_name(input, uni.name, name_buffer_, name_buffer_offset);
|
||||
input->location = glGetUniformLocation(program, name_buffer_ + input->name_offset);
|
||||
/* Until we make use of explicit uniform location. */
|
||||
if (true || !GLContext::explicit_location_support) {
|
||||
input->location = glGetUniformLocation(program, name_buffer_ + input->name_offset);
|
||||
}
|
||||
input->binding = -1;
|
||||
input++;
|
||||
}
|
||||
|
@@ -29,11 +29,11 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_area_borders)
|
||||
.vertex_in(0, Type::VEC2, "pos")
|
||||
.vertex_out(smooth_uv_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::VEC4, "rect")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(Type::FLOAT, "scale")
|
||||
.push_constant(Type::INT, "cornerLen")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::VEC4, "rect")
|
||||
.push_constant(20, Type::VEC4, "color")
|
||||
.push_constant(24, Type::FLOAT, "scale")
|
||||
.push_constant(25, Type::INT, "cornerLen")
|
||||
.vertex_source("gpu_shader_2D_area_borders_vert.glsl")
|
||||
.fragment_source("gpu_shader_2D_area_borders_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -26,10 +26,10 @@
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_2D_checker)
|
||||
.vertex_in(0, Type::VEC2, "pos")
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::VEC4, "color1")
|
||||
.push_constant(Type::VEC4, "color2")
|
||||
.push_constant(Type::INT, "size")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::VEC4, "color1")
|
||||
.push_constant(20, Type::VEC4, "color2")
|
||||
.push_constant(24, Type::INT, "size")
|
||||
.vertex_source("gpu_shader_2D_vert.glsl")
|
||||
.fragment_source("gpu_shader_checker_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -26,11 +26,11 @@
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_2D_diag_stripes)
|
||||
.vertex_in(0, Type::VEC2, "pos")
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::VEC4, "color1")
|
||||
.push_constant(Type::VEC4, "color2")
|
||||
.push_constant(Type::INT, "size1")
|
||||
.push_constant(Type::INT, "size2")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::VEC4, "color1")
|
||||
.push_constant(20, Type::VEC4, "color2")
|
||||
.push_constant(24, Type::INT, "size1")
|
||||
.push_constant(28, Type::INT, "size2")
|
||||
.vertex_source("gpu_shader_2D_vert.glsl")
|
||||
.fragment_source("gpu_shader_diag_stripes_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -30,7 +30,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_flat_color)
|
||||
.vertex_in(1, Type::VEC4, "color")
|
||||
.vertex_out(flat_color_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_2D_flat_color_vert.glsl")
|
||||
.fragment_source("gpu_shader_flat_color_frag.glsl")
|
||||
.additional_info("gpu_srgb_to_framebuffer_space")
|
||||
|
@@ -25,6 +25,6 @@
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_color)
|
||||
.additional_info("gpu_shader_2D_image_common")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(16, Type::VEC4, "color")
|
||||
.fragment_source("gpu_shader_image_color_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -25,7 +25,7 @@
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_desaturate_color)
|
||||
.additional_info("gpu_shader_2D_image_common")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(Type::FLOAT, "factor")
|
||||
.push_constant(16, Type::VEC4, "color")
|
||||
.push_constant(20, Type::FLOAT, "factor")
|
||||
.fragment_source("gpu_shader_image_desaturate_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_common)
|
||||
.vertex_in(1, Type::VEC2, "texCoord")
|
||||
.vertex_out(smooth_tex_coord_interp_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.sampler(0, ImageType::FLOAT_2D, "image")
|
||||
.vertex_source("gpu_shader_2D_image_vert.glsl");
|
||||
|
||||
|
@@ -29,9 +29,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_merge)
|
||||
.vertex_in(1, Type::VEC2, "texCoord")
|
||||
.vertex_out(smooth_tex_coord_interp_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::BOOL, "display_transform")
|
||||
.push_constant(Type::BOOL, "overlay")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::BOOL, "display_transform")
|
||||
.push_constant(17, Type::BOOL, "overlay")
|
||||
.sampler(0, ImageType::FLOAT_2D, "image_texture")
|
||||
.sampler(1, ImageType::FLOAT_2D, "overlays_texture")
|
||||
.vertex_source("gpu_shader_2D_image_vert.glsl")
|
||||
|
@@ -29,8 +29,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_stereo_merge)
|
||||
.fragment_out(1, Type::VEC4, "overlayColor")
|
||||
.sampler(0, ImageType::FLOAT_2D, "imageTexture")
|
||||
.sampler(1, ImageType::FLOAT_2D, "overlayTexture")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::INT, "stereoDisplaySettings")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::INT, "stereoDisplaySettings")
|
||||
.vertex_source("gpu_shader_2D_vert.glsl")
|
||||
.fragment_source("gpu_shader_image_overlays_stereo_merge_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -27,10 +27,10 @@
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_rect_color)
|
||||
.vertex_out(smooth_tex_coord_interp_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(Type::VEC4, "rect_icon")
|
||||
.push_constant(Type::VEC4, "rect_geom")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::VEC4, "color")
|
||||
.push_constant(20, Type::VEC4, "rect_icon")
|
||||
.push_constant(24, Type::VEC4, "rect_geom")
|
||||
.sampler(0, ImageType::FLOAT_2D, "image")
|
||||
.vertex_source("gpu_shader_2D_image_rect_vert.glsl")
|
||||
.fragment_source("gpu_shader_image_color_frag.glsl")
|
||||
|
@@ -25,7 +25,7 @@
|
||||
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_shuffle_color)
|
||||
.additional_info("gpu_shader_2D_image_common")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(Type::VEC4, "shuffle")
|
||||
.push_constant(16, Type::VEC4, "color")
|
||||
.push_constant(20, Type::VEC4, "shuffle")
|
||||
.fragment_source("gpu_shader_image_shuffle_color_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -28,7 +28,7 @@
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_2D_line_dashed_uniform_color)
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.vertex_out(flat_color_iface)
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_2D_line_dashed_uniform_color_vert.glsl")
|
||||
.fragment_source("gpu_shader_2D_line_dashed_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -39,7 +39,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_nodelink)
|
||||
.vertex_out(nodelink_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.uniform_buf(0, "NodeLinkData", "node_link_data", Frequency::PASS)
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_2D_nodelink_vert.glsl")
|
||||
.fragment_source("gpu_shader_2D_nodelink_frag.glsl")
|
||||
.typedef_source("GPU_shader_shared.h")
|
||||
@@ -64,7 +64,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_nodelink_inst)
|
||||
.vertex_out(nodelink_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.uniform_buf(0, "NodeLinkInstanceData", "node_link_data", Frequency::PASS)
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_2D_nodelink_vert.glsl")
|
||||
.fragment_source("gpu_shader_2D_nodelink_frag.glsl")
|
||||
.typedef_source("GPU_shader_shared.h")
|
||||
|
@@ -28,9 +28,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_aa)
|
||||
.vertex_in(0, Type::VEC2, "pos")
|
||||
.vertex_out(smooth_radii_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(Type::FLOAT, "size")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::VEC4, "color")
|
||||
.push_constant(20, Type::FLOAT, "size")
|
||||
.vertex_source("gpu_shader_2D_point_uniform_size_aa_vert.glsl")
|
||||
.fragment_source("gpu_shader_point_uniform_color_aa_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -28,11 +28,11 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_uniform_size_uniform_color_outline_aa
|
||||
.vertex_in(0, Type::VEC2, "pos")
|
||||
.vertex_out(smooth_radii_outline_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(Type::VEC4, "outlineColor")
|
||||
.push_constant(Type::FLOAT, "size")
|
||||
.push_constant(Type::FLOAT, "outlineWidth")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(20, Type::VEC4, "color")
|
||||
.push_constant(24, Type::VEC4, "outlineColor")
|
||||
.push_constant(28, Type::FLOAT, "size")
|
||||
.push_constant(29, Type::FLOAT, "outlineWidth")
|
||||
.vertex_source("gpu_shader_2D_point_uniform_size_outline_aa_vert.glsl")
|
||||
.fragment_source("gpu_shader_point_uniform_color_outline_aa_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -30,7 +30,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_point_varying_size_varying_color)
|
||||
.vertex_in(2, Type::VEC4, "color")
|
||||
.vertex_out(smooth_color_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_2D_point_varying_size_varying_color_vert.glsl")
|
||||
.fragment_source("gpu_shader_point_varying_color_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_smooth_color)
|
||||
.vertex_in(1, Type::VEC4, "color")
|
||||
.vertex_out(smooth_color_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_2D_smooth_color_vert.glsl")
|
||||
.fragment_source("gpu_shader_2D_smooth_color_frag.glsl")
|
||||
.additional_info("gpu_srgb_to_framebuffer_space")
|
||||
|
@@ -26,8 +26,8 @@
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_2D_uniform_color)
|
||||
.vertex_in(0, Type::VEC2, "pos")
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::VEC4, "color")
|
||||
.vertex_source("gpu_shader_2D_vert.glsl")
|
||||
.fragment_source("gpu_shader_uniform_color_frag.glsl")
|
||||
.additional_info("gpu_srgb_to_framebuffer_space")
|
||||
|
@@ -27,7 +27,7 @@
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_3D_depth_only)
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.vertex_out(flat_color_iface)
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_3D_vert.glsl")
|
||||
.fragment_source("gpu_shader_depth_only_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -29,7 +29,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_flat_color)
|
||||
.vertex_in(1, Type::VEC4, "color")
|
||||
.vertex_out(flat_color_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_3D_flat_color_vert.glsl")
|
||||
.fragment_source("gpu_shader_flat_color_frag.glsl")
|
||||
.additional_info("gpu_srgb_to_framebuffer_space")
|
||||
|
@@ -29,8 +29,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_image_modulate_alpha)
|
||||
.vertex_in(1, Type::VEC2, "texCoord")
|
||||
.vertex_out(smooth_tex_coord_interp_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::FLOAT, "alpha")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::FLOAT, "alpha")
|
||||
.sampler(0, ImageType::FLOAT_2D, "image", Frequency::PASS)
|
||||
.vertex_source("gpu_shader_3D_image_vert.glsl")
|
||||
.fragment_source("gpu_shader_image_modulate_alpha_frag.glsl")
|
||||
|
@@ -28,7 +28,7 @@
|
||||
GPU_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color)
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.vertex_out(flat_color_iface)
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_3D_line_dashed_uniform_color_vert.glsl")
|
||||
.fragment_source("gpu_shader_2D_line_dashed_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
@@ -28,7 +28,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_fixed_size_varying_color)
|
||||
.vertex_in(1, Type::VEC4, "color")
|
||||
.vertex_out(smooth_color_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_3D_point_fixed_size_varying_color_vert.glsl")
|
||||
.fragment_source("gpu_shader_point_varying_color_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
@@ -39,7 +39,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_varying_size_varying_color)
|
||||
.vertex_in(2, Type::FLOAT, "size")
|
||||
.vertex_out(smooth_color_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.vertex_source("gpu_shader_3D_point_varying_size_varying_color_vert.glsl")
|
||||
.fragment_source("gpu_shader_point_varying_color_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
@@ -48,9 +48,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_3D_point_uniform_size_uniform_color_aa)
|
||||
.vertex_in(0, Type::VEC3, "pos")
|
||||
.vertex_out(smooth_radii_iface)
|
||||
.fragment_out(0, Type::VEC4, "fragColor")
|
||||
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(Type::VEC4, "color")
|
||||
.push_constant(Type::FLOAT, "size")
|
||||
.push_constant(0, Type::MAT4, "ModelViewProjectionMatrix")
|
||||
.push_constant(16, Type::VEC4, "color")
|
||||
.push_constant(20, Type::FLOAT, "size")
|
||||
.vertex_source("gpu_shader_3D_point_uniform_size_aa_vert.glsl")
|
||||
.fragment_source("gpu_shader_point_uniform_color_aa_frag.glsl")
|
||||
.do_static_compilation(true);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user