Add Chromium-only Blender WebEngine parity work
This commit is contained in:
823
blender-5.2.0/source/blender/gpu/opengl/gl_backend.cc
Normal file
823
blender-5.2.0/source/blender/gpu/opengl/gl_backend.cc
Normal file
@@ -0,0 +1,823 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#if defined(WIN32)
|
||||
# include "BLI_winstuff.h"
|
||||
#endif
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_subprocess.hh"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "gpu_capabilities_private.hh"
|
||||
#include "gpu_platform_private.hh"
|
||||
|
||||
#include "gl_debug.hh"
|
||||
|
||||
#include "gl_backend.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.opengl"};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Platform
|
||||
* \{ */
|
||||
|
||||
static bool match_renderer(StringRef renderer, const Vector<std::string> &items)
|
||||
{
|
||||
for (const std::string &item : items) {
|
||||
const std::string wrapped = " " + item + " ";
|
||||
if (renderer.endswith(item) || renderer.find(wrapped) != StringRef::not_found) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool parse_version(const std::string &version,
|
||||
const std::string &format,
|
||||
Vector<int> &r_version)
|
||||
{
|
||||
int f = 0;
|
||||
std::string subversion;
|
||||
for (int v : IndexRange(version.size())) {
|
||||
bool match = false;
|
||||
if (format[f] == '0') {
|
||||
if (std::isdigit(version[v])) {
|
||||
match = true;
|
||||
subversion.push_back(version[v]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
match = version[v] == format[f];
|
||||
if (!subversion.empty()) {
|
||||
r_version.append(std::stoi(subversion));
|
||||
subversion.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
f = 0;
|
||||
subversion.clear();
|
||||
r_version.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
f++;
|
||||
|
||||
if (f == format.size()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Try to check if the driver is older than 22.6.1, preferring false positives. */
|
||||
static bool is_bad_AMD_driver(const char *version_cstr)
|
||||
{
|
||||
std::string version_str = version_cstr;
|
||||
/* Allow matches when the version number is at the string end. */
|
||||
version_str.push_back(' ');
|
||||
|
||||
Vector<int> version;
|
||||
|
||||
if (parse_version(version_str, " 00.00.00.00 ", version) ||
|
||||
parse_version(version_str, " 00.00.0.000000 ", version) ||
|
||||
parse_version(version_str, " 00.00.00.000000 ", version) ||
|
||||
parse_version(version_str, " 00.00.000000 ", version) ||
|
||||
parse_version(version_str, " 00.00.00 ", version) ||
|
||||
parse_version(version_str, " 00.00.0 ", version) ||
|
||||
parse_version(version_str, " 00.0.00 ", version) ||
|
||||
parse_version(version_str, " 00.Q0.", version))
|
||||
{
|
||||
return version[0] < 23;
|
||||
}
|
||||
/* Some drivers only expose the Windows version https://gpuopen.com/version-table/ */
|
||||
if (parse_version(version_str, " 00.00.00000.00000 ", version) ||
|
||||
parse_version(version_str, " 00.00.00000.0000 ", version) ||
|
||||
parse_version(version_str, " 00.00.0000.00000 ", version))
|
||||
{
|
||||
return version[0] < 31 || (version[0] == 31 && version[2] < 21001);
|
||||
}
|
||||
|
||||
/* Unknown version, assume it's a bad one. */
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLBackend::platform_init()
|
||||
{
|
||||
BLI_assert(!GPG.initialized);
|
||||
|
||||
const char *vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
|
||||
const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
|
||||
const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
|
||||
GPUDeviceType device = GPU_DEVICE_ANY;
|
||||
GPUOSType os = GPU_OS_ANY;
|
||||
GPUDriverType driver = GPU_DRIVER_ANY;
|
||||
GPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED;
|
||||
|
||||
#ifdef _WIN32
|
||||
os = GPU_OS_WIN;
|
||||
#else
|
||||
os = GPU_OS_UNIX;
|
||||
#endif
|
||||
|
||||
if (!vendor) {
|
||||
printf("Warning: No OpenGL vendor detected.\n");
|
||||
device = GPU_DEVICE_UNKNOWN;
|
||||
driver = GPU_DRIVER_ANY;
|
||||
}
|
||||
else if (strstr(renderer, "Mesa DRI R") ||
|
||||
(strstr(renderer, "Radeon") && (strstr(vendor, "X.Org") || strstr(version, "Mesa"))) ||
|
||||
(strstr(renderer, "AMD") && (strstr(vendor, "X.Org") || strstr(version, "Mesa"))) ||
|
||||
(strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) ||
|
||||
(strstr(renderer, "Gallium ") && strstr(renderer, " on AMD ")))
|
||||
{
|
||||
device = GPU_DEVICE_ATI;
|
||||
driver = GPU_DRIVER_OPENSOURCE;
|
||||
}
|
||||
else if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
|
||||
device = GPU_DEVICE_ATI;
|
||||
driver = GPU_DRIVER_OFFICIAL;
|
||||
}
|
||||
else if (strstr(vendor, "NVIDIA")) {
|
||||
device = GPU_DEVICE_NVIDIA;
|
||||
driver = GPU_DRIVER_OFFICIAL;
|
||||
}
|
||||
else if (strstr(vendor, "Intel") ||
|
||||
/* src/mesa/drivers/dri/intel/intel_context.c */
|
||||
strstr(renderer, "Mesa DRI Intel") || strstr(renderer, "Mesa DRI Mobile Intel"))
|
||||
{
|
||||
device = GPU_DEVICE_INTEL;
|
||||
driver = GPU_DRIVER_OFFICIAL;
|
||||
|
||||
if (strstr(renderer, "UHD Graphics") ||
|
||||
/* Not UHD but affected by the same bugs. */
|
||||
strstr(renderer, "HD Graphics 530") || strstr(renderer, "Kaby Lake GT2") ||
|
||||
strstr(renderer, "Whiskey Lake"))
|
||||
{
|
||||
device |= GPU_DEVICE_INTEL_UHD;
|
||||
}
|
||||
}
|
||||
else if (strstr(renderer, "Nouveau") || strstr(vendor, "nouveau")) {
|
||||
device = GPU_DEVICE_NVIDIA;
|
||||
driver = GPU_DRIVER_OPENSOURCE;
|
||||
}
|
||||
else if (strstr(vendor, "Mesa")) {
|
||||
device = GPU_DEVICE_SOFTWARE;
|
||||
driver = GPU_DRIVER_SOFTWARE;
|
||||
}
|
||||
else if (strstr(vendor, "Microsoft")) {
|
||||
/* Qualcomm devices use Mesa's GLOn12, which claims to be vended by Microsoft */
|
||||
if (strstr(renderer, "Qualcomm")) {
|
||||
device = GPU_DEVICE_QUALCOMM;
|
||||
driver = GPU_DRIVER_OFFICIAL;
|
||||
}
|
||||
else {
|
||||
device = GPU_DEVICE_SOFTWARE;
|
||||
driver = GPU_DRIVER_SOFTWARE;
|
||||
}
|
||||
}
|
||||
else if (strstr(vendor, "Apple")) {
|
||||
/* Apple Silicon. */
|
||||
device = GPU_DEVICE_APPLE;
|
||||
driver = GPU_DRIVER_OFFICIAL;
|
||||
}
|
||||
else if (strstr(renderer, "Apple Software Renderer")) {
|
||||
device = GPU_DEVICE_SOFTWARE;
|
||||
driver = GPU_DRIVER_SOFTWARE;
|
||||
}
|
||||
else if (strstr(renderer, "llvmpipe") || strstr(renderer, "softpipe")) {
|
||||
device = GPU_DEVICE_SOFTWARE;
|
||||
driver = GPU_DRIVER_SOFTWARE;
|
||||
}
|
||||
else {
|
||||
printf("Warning: Could not find a matching GPU name. Things may not behave as expected.\n");
|
||||
printf("Detected OpenGL configuration:\n");
|
||||
printf("Vendor: %s\n", vendor);
|
||||
printf("Renderer: %s\n", renderer);
|
||||
}
|
||||
|
||||
/* Detect support level */
|
||||
if (!(epoxy_gl_version() >= 43)) {
|
||||
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
}
|
||||
else {
|
||||
#if defined(WIN32)
|
||||
long long driverVersion = 0;
|
||||
if (device & GPU_DEVICE_QUALCOMM) {
|
||||
if (BLI_windows_get_directx_driver_version(L"Qualcomm(R) Adreno(TM)", &driverVersion)) {
|
||||
/* Parse out the driver version in format x.x.x.x */
|
||||
WORD ver0 = (driverVersion >> 48) & 0xffff;
|
||||
WORD ver1 = (driverVersion >> 32) & 0xffff;
|
||||
WORD ver2 = (driverVersion >> 16) & 0xffff;
|
||||
|
||||
/* Any Qualcomm driver older than 30.x.x.x will never capable of running blender >= 4.0
|
||||
* As due to an issue in D3D typed UAV load capabilities, Compute Shaders are not available
|
||||
* 30.0.3820.x and above are capable of running blender >=4.0, but these drivers
|
||||
* are only available on 8cx gen3 devices or newer */
|
||||
if (ver0 < 30 || (ver0 == 30 && ver1 == 0 && ver2 < 3820)) {
|
||||
std::cout
|
||||
<< "=====================================\n"
|
||||
<< "Qualcomm drivers older than 30.0.3820.x cannot run Blender 4.0 or later.\n"
|
||||
<< "If your device is older than an 8cx Gen3, you must use a 3.x LTS release.\n"
|
||||
<< "If you have an 8cx Gen3 or newer device, a driver update may be available.\n"
|
||||
<< "=====================================\n";
|
||||
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ((device & GPU_DEVICE_INTEL) && (os & GPU_OS_WIN)) {
|
||||
/* Old Intel drivers with known bugs that cause material properties to crash.
|
||||
* Version Build 10.18.14.5067 is the latest available and appears to be working
|
||||
* ok with our workarounds, so excluded from this list. */
|
||||
if (strstr(version, "Build 7.14") || strstr(version, "Build 7.15") ||
|
||||
strstr(version, "Build 8.15") || strstr(version, "Build 9.17") ||
|
||||
strstr(version, "Build 9.18") || strstr(version, "Build 10.18.10.3") ||
|
||||
strstr(version, "Build 10.18.10.4") || strstr(version, "Build 10.18.10.5") ||
|
||||
strstr(version, "Build 10.18.14.4"))
|
||||
{
|
||||
support_level = GPU_SUPPORT_LEVEL_LIMITED;
|
||||
}
|
||||
/* A rare GPU that has z-fighting issues in edit mode. (see #128179) */
|
||||
if (strstr(renderer, "HD Graphics 405")) {
|
||||
support_level = GPU_SUPPORT_LEVEL_LIMITED;
|
||||
}
|
||||
/* Latest Intel driver have bugs that won't allow Blender to start.
|
||||
* Users must install different version of the driver.
|
||||
* See #113124 for more information. */
|
||||
if (strstr(version, "Build 20.19.15.51")) {
|
||||
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
if ((device & GPU_DEVICE_ATI) && (os & GPU_OS_UNIX)) {
|
||||
/* Platform seems to work when SB backend is disabled. This can be done
|
||||
* by adding the environment variable `R600_DEBUG=nosb`. */
|
||||
if (strstr(renderer, "AMD CEDAR")) {
|
||||
support_level = GPU_SUPPORT_LEVEL_LIMITED;
|
||||
}
|
||||
}
|
||||
if ((device & GPU_DEVICE_QUALCOMM) && (os & GPU_OS_WIN)) {
|
||||
if (strstr(version, "Mesa 20.") || strstr(version, "Mesa 21.") ||
|
||||
strstr(version, "Mesa 22.") || strstr(version, "Mesa 23."))
|
||||
{
|
||||
std::cerr << "Unsupported driver. Requires at least Mesa 24.0.0." << std::endl;
|
||||
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check SSBO bindings requirement. */
|
||||
GLint max_ssbo_binds_vertex;
|
||||
GLint max_ssbo_binds_fragment;
|
||||
GLint max_ssbo_binds_compute;
|
||||
glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &max_ssbo_binds_vertex);
|
||||
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &max_ssbo_binds_fragment);
|
||||
glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &max_ssbo_binds_compute);
|
||||
GLint max_ssbo_binds = std::min(
|
||||
{max_ssbo_binds_vertex, max_ssbo_binds_fragment, max_ssbo_binds_compute});
|
||||
if (max_ssbo_binds < 12) {
|
||||
std::cout << "Warning: Unsupported platform as it supports max " << max_ssbo_binds
|
||||
<< " SSBO binding locations\n";
|
||||
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (!epoxy_has_gl_extension("GL_ARB_shader_draw_parameters")) {
|
||||
std::cout << "Error: The OpenGL implementation doesn't support ARB_shader_draw_parameters\n";
|
||||
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (!epoxy_has_gl_extension("GL_ARB_clip_control")) {
|
||||
std::cout << "Error: The OpenGL implementation doesn't support ARB_clip_control\n";
|
||||
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute shaders have some issues with those versions (see #94936). */
|
||||
if ((device & GPU_DEVICE_ATI) && (driver & GPU_DRIVER_OFFICIAL) &&
|
||||
(strstr(version, "4.5.14831") || strstr(version, "4.5.14760")))
|
||||
{
|
||||
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
|
||||
}
|
||||
|
||||
GPG.init(device,
|
||||
os,
|
||||
driver,
|
||||
support_level,
|
||||
GPU_BACKEND_OPENGL,
|
||||
vendor,
|
||||
renderer,
|
||||
version,
|
||||
GPU_ARCHITECTURE_IMR);
|
||||
|
||||
GPG.devices.append(
|
||||
{.identifier = "OPENGL", .index = 0, .vendor_id = 0, .device_id = 0, .name = renderer});
|
||||
|
||||
GPG.device_uuid.reinitialize(0);
|
||||
GPG.device_luid.reinitialize(0);
|
||||
GPG.device_luid_node_mask = 0;
|
||||
|
||||
if (epoxy_has_gl_extension("GL_EXT_memory_object")) {
|
||||
GLint number_of_devices = 0;
|
||||
glGetIntegerv(GL_NUM_DEVICE_UUIDS_EXT, &number_of_devices);
|
||||
/* Multiple devices could be used by the context if certain extensions like multi-cast is used.
|
||||
* But this is not used by Blender, so this should always be 1. */
|
||||
BLI_assert(number_of_devices == 1);
|
||||
|
||||
GLubyte device_uuid[GL_UUID_SIZE_EXT] = {0};
|
||||
glGetUnsignedBytei_vEXT(GL_DEVICE_UUID_EXT, 0, device_uuid);
|
||||
GPG.device_uuid = Array<uint8_t, 16>(Span<uint8_t>(device_uuid, GL_UUID_SIZE_EXT));
|
||||
|
||||
/* LUID is only supported on Windows. */
|
||||
if (epoxy_has_gl_extension("GL_EXT_memory_object_win32") && (os & GPU_OS_WIN)) {
|
||||
GLubyte device_luid[GL_LUID_SIZE_EXT] = {0};
|
||||
glGetUnsignedBytevEXT(GL_DEVICE_LUID_EXT, device_luid);
|
||||
GPG.device_luid = Array<uint8_t, 8>(Span<uint8_t>(device_luid, GL_LUID_SIZE_EXT));
|
||||
|
||||
GLint node_mask = 0;
|
||||
glGetIntegerv(GL_DEVICE_NODE_MASK_EXT, &node_mask);
|
||||
GPG.device_luid_node_mask = uint32_t(node_mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::platform_exit()
|
||||
{
|
||||
BLI_assert(GPG.initialized);
|
||||
GPG.clear();
|
||||
}
|
||||
|
||||
TexturePool *GLBackend::texturepool_alloc()
|
||||
{
|
||||
if (GCaps.texture_pool_workaround) {
|
||||
CLOG_TRACE(&LOG, "Using texture pool \"TexturePoolImpl\".");
|
||||
return new TexturePoolImpl();
|
||||
}
|
||||
|
||||
CLOG_TRACE(&LOG, "Using texture pool \"GLTexturePool\".");
|
||||
return new GLTexturePool();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Capabilities
|
||||
* \{ */
|
||||
|
||||
static const char *gl_extension_get(int i)
|
||||
{
|
||||
return reinterpret_cast<char *>(const_cast<GLubyte *>(glGetStringi(GL_EXTENSIONS, i)));
|
||||
}
|
||||
|
||||
static void detect_workarounds()
|
||||
{
|
||||
const char *vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
|
||||
const char *renderer = reinterpret_cast<const char *>(glGetString(GL_RENDERER));
|
||||
const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
|
||||
|
||||
if (G.debug & G_DEBUG_GPU_FORCE_WORKAROUNDS) {
|
||||
printf("\n");
|
||||
printf("GL: Forcing workaround usage and disabling extensions.\n");
|
||||
printf(" OpenGL identification strings\n");
|
||||
printf(" vendor: %s\n", vendor);
|
||||
printf(" renderer: %s\n", renderer);
|
||||
printf(" version: %s\n\n", version);
|
||||
GCaps.depth_blitting_workaround = true;
|
||||
GCaps.stencil_clasify_buffer_workaround = true;
|
||||
GCaps.texture_pool_workaround = true;
|
||||
GLContext::debug_layer_workaround = true;
|
||||
/* Turn off Blender features. */
|
||||
GCaps.hdr_viewport_support = false;
|
||||
/* Turn off OpenGL 4.4 features. */
|
||||
GLContext::multi_bind_support = false;
|
||||
GLContext::multi_bind_image_support = false;
|
||||
/* Turn off OpenGL 4.5 features. */
|
||||
GLContext::direct_state_access_support = false;
|
||||
GLContext::derivative_control_support = false;
|
||||
/* Turn off OpenGL 4.6 features. */
|
||||
GLContext::texture_filter_anisotropic_support = false;
|
||||
/* Turn off extensions. */
|
||||
GLContext::layered_rendering_support = false;
|
||||
GLContext::vertex_shader_viewport_index_support = false;
|
||||
GLContext::vertex_shader_layer_support = false;
|
||||
/* Turn off vendor specific extensions. */
|
||||
GLContext::native_barycentric_support = false;
|
||||
GLContext::framebuffer_fetch_support = false;
|
||||
GLContext::texture_barrier_support = false;
|
||||
GCaps.stencil_export_support = false;
|
||||
|
||||
#if 0
|
||||
/* Do not alter OpenGL 4.3 features.
|
||||
* These code paths should be removed. */
|
||||
GLContext::debug_layer_support = false;
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) &&
|
||||
(strstr(version, "4.5.13399") || strstr(version, "4.5.13417") ||
|
||||
strstr(version, "4.5.13422") || strstr(version, "4.5.13467")))
|
||||
{
|
||||
/* The renderers include:
|
||||
* Radeon HD 5000;
|
||||
* Radeon HD 7500M;
|
||||
* Radeon HD 7570M;
|
||||
* Radeon HD 7600M;
|
||||
* Radeon R5 Graphics;
|
||||
* And others... */
|
||||
GLContext::unused_fb_slot_workaround = true;
|
||||
}
|
||||
/* We have issues with this specific renderer. (see #74024) */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) &&
|
||||
(strstr(renderer, "AMD VERDE") || strstr(renderer, "AMD KAVERI") ||
|
||||
strstr(renderer, "AMD TAHITI")))
|
||||
{
|
||||
GLContext::unused_fb_slot_workaround = true;
|
||||
}
|
||||
/* See #82856: AMD drivers since 20.11 running on a polaris architecture doesn't support the
|
||||
* `GL_INT_2_10_10_10_REV` data type correctly. This data type is used to pack normals and flags.
|
||||
* The work around uses `TextureFormat::SINT_16_16_16_16`. In 22.?.? drivers this
|
||||
* has been fixed for polaris platform. Keeping legacy platforms around just in case.
|
||||
*/
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
||||
/* Check for AMD legacy driver. Assuming that when these drivers are used this bug is present.
|
||||
*/
|
||||
if (is_bad_AMD_driver(version)) {
|
||||
GCaps.use_hq_normals_workaround = true;
|
||||
}
|
||||
const Vector<std::string> matches = {
|
||||
"RX550/550", "(TM) 520", "(TM) 530", "(TM) 535", "R5", "R7", "R9", "HD"};
|
||||
|
||||
if (match_renderer(renderer, matches)) {
|
||||
GCaps.use_hq_normals_workaround = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Maybe not all of these drivers have problems with `GL_ARB_base_instance`.
|
||||
* But it's hard to test each case.
|
||||
* We get crashes from some crappy Intel drivers don't work well with shaders created in
|
||||
* different rendering contexts. */
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY) &&
|
||||
(strstr(version, "Build 10.18.10.3") || strstr(version, "Build 10.18.10.4") ||
|
||||
strstr(version, "Build 10.18.10.5") || strstr(version, "Build 10.18.14.4") ||
|
||||
strstr(version, "Build 10.18.14.5")))
|
||||
{
|
||||
GCaps.use_main_context_workaround = true;
|
||||
}
|
||||
/* Somehow fixes armature display issues (see #69743). */
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY) &&
|
||||
strstr(version, "Build 20.19.15.4285"))
|
||||
{
|
||||
GCaps.use_main_context_workaround = true;
|
||||
}
|
||||
/* Needed to avoid driver hangs on legacy AMD drivers (see #139939). */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) &&
|
||||
is_bad_AMD_driver(version))
|
||||
{
|
||||
GCaps.use_main_context_workaround = true;
|
||||
}
|
||||
/* See #70187: merging vertices fail. This has been tested from `18.2.2` till `19.3.0~dev`
|
||||
* of the Mesa driver */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) &&
|
||||
(strstr(version, "Mesa 18.") || strstr(version, "Mesa 19.0") ||
|
||||
strstr(version, "Mesa 19.1") || strstr(version, "Mesa 19.2")))
|
||||
{
|
||||
GLContext::unused_fb_slot_workaround = true;
|
||||
}
|
||||
|
||||
/* Snapdragon X Elite devices currently have a driver bug that results in
|
||||
* eevee rendering a black cube with anything except an emission shader
|
||||
* if shader draw parameters are enabled (#122837) */
|
||||
#if defined(WIN32)
|
||||
long long driverVersion = 0;
|
||||
if (GPU_type_matches(GPU_DEVICE_QUALCOMM, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
if (BLI_windows_get_directx_driver_version(L"Qualcomm(R) Adreno(TM)", &driverVersion)) {
|
||||
/* Parse out the driver version */
|
||||
WORD ver0 = (driverVersion >> 48) & 0xffff;
|
||||
|
||||
/* X Elite devices have GPU driver version 31, and currently no known release version of the
|
||||
* GPU driver renders the cube correctly. This will be changed when a working driver version
|
||||
* is released to commercial devices to only enable this flags on older drivers. */
|
||||
if (ver0 == 31) {
|
||||
GCaps.stencil_clasify_buffer_workaround = true;
|
||||
}
|
||||
|
||||
/* Disable OpenGL texture pool on Snapdragon 8cx Gen 3 devices. See #142229. We assume that
|
||||
* these devices use driver 30.x.x.x */
|
||||
if (ver0 == 30) {
|
||||
GCaps.texture_pool_workaround = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Enable our own incomplete debug layer if no other is available. */
|
||||
if (GLContext::debug_layer_support == false) {
|
||||
GLContext::debug_layer_workaround = true;
|
||||
}
|
||||
|
||||
/* There is an issue in AMD official driver where we cannot use multi bind when using images. AMD
|
||||
* is aware of the issue, but hasn't released a fix. */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
||||
GLContext::multi_bind_image_support = false;
|
||||
}
|
||||
|
||||
/* #107642, #120273 Windows Intel iGPU (multiple generations) incorrectly report that
|
||||
* they support image binding. But when used it results into `GL_INVALID_OPERATION` with
|
||||
* `internal format of texture N is not supported`. */
|
||||
if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL)) {
|
||||
GLContext::multi_bind_image_support = false;
|
||||
}
|
||||
|
||||
if (G.debug & G_DEBUG_GPU_NO_TEXTURE_POOL) {
|
||||
GCaps.texture_pool_workaround = true;
|
||||
}
|
||||
|
||||
/* Disable texture pool on any Intel driver; glTextureView is inconsistently
|
||||
* broken on Intel HD and newer integrated cards, and output of the vendor string doesn't
|
||||
* differentiate e.g. an Arc V140 from an Arc B750 :( */
|
||||
if ((GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY) ||
|
||||
GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_ANY, GPU_DRIVER_ANY)))
|
||||
{
|
||||
GCaps.texture_pool_workaround = true;
|
||||
}
|
||||
|
||||
/* Disable texture pool on closed source AMD driver; glTextureView
|
||||
* breaks frame-buffers for several formats. This is not an issue on Mesa. */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
||||
GCaps.texture_pool_workaround = true;
|
||||
}
|
||||
|
||||
/* Metal-related Workarounds. */
|
||||
|
||||
/* Minimum Per-Vertex stride is 1 byte for OpenGL. */
|
||||
GCaps.minimum_per_vertex_stride = 1;
|
||||
}
|
||||
|
||||
/** Internal capabilities. */
|
||||
|
||||
GLint GLContext::max_cubemap_size = 0;
|
||||
GLint GLContext::max_ubo_binds = 0;
|
||||
GLint GLContext::max_ssbo_binds = 0;
|
||||
|
||||
/** Extensions. */
|
||||
|
||||
bool GLContext::debug_layer_support = false;
|
||||
bool GLContext::direct_state_access_support = false;
|
||||
bool GLContext::explicit_location_support = false;
|
||||
bool GLContext::framebuffer_fetch_support = false;
|
||||
bool GLContext::layered_rendering_support = false;
|
||||
bool GLContext::vertex_shader_viewport_index_support = false;
|
||||
bool GLContext::vertex_shader_layer_support = false;
|
||||
bool GLContext::native_barycentric_support = false;
|
||||
bool GLContext::multi_bind_support = false;
|
||||
bool GLContext::multi_bind_image_support = false;
|
||||
bool GLContext::stencil_texturing_support = false;
|
||||
bool GLContext::texture_barrier_support = false;
|
||||
bool GLContext::texture_filter_anisotropic_support = false;
|
||||
bool GLContext::derivative_control_support = false;
|
||||
|
||||
/** Workarounds. */
|
||||
|
||||
bool GLContext::debug_layer_workaround = false;
|
||||
bool GLContext::unused_fb_slot_workaround = false;
|
||||
bool GLContext::generate_mipmap_workaround = false;
|
||||
|
||||
void GLBackend::capabilities_init()
|
||||
{
|
||||
BLI_assert(epoxy_gl_version() >= 33);
|
||||
/* Common Capabilities. */
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &GCaps.max_texture_size);
|
||||
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &GCaps.max_texture_layers);
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &GCaps.max_textures);
|
||||
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &GCaps.max_uniforms_vert);
|
||||
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &GCaps.max_uniforms_frag);
|
||||
glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &GCaps.max_batch_indices);
|
||||
glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &GCaps.max_batch_vertices);
|
||||
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &GCaps.max_vertex_attribs);
|
||||
glGetIntegerv(GL_MAX_VARYING_FLOATS, &GCaps.max_varying_floats);
|
||||
glGetIntegerv(GL_MAX_IMAGE_UNITS, &GCaps.max_images);
|
||||
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &GCaps.extensions_len);
|
||||
GCaps.extension_get = gl_extension_get;
|
||||
|
||||
GCaps.mem_stats_support = epoxy_has_gl_extension("GL_NVX_gpu_memory_info") ||
|
||||
epoxy_has_gl_extension("GL_ATI_meminfo");
|
||||
GCaps.geometry_shader_support = true;
|
||||
GCaps.hdr_viewport_support = false;
|
||||
|
||||
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &GCaps.max_work_group_count[0]);
|
||||
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &GCaps.max_work_group_count[1]);
|
||||
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &GCaps.max_work_group_count[2]);
|
||||
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]);
|
||||
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]);
|
||||
glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]);
|
||||
glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &GCaps.max_shader_storage_buffer_bindings);
|
||||
glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &GCaps.max_compute_shader_storage_blocks);
|
||||
int64_t max_ssbo_size, max_ubo_size;
|
||||
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_ubo_size);
|
||||
GCaps.max_uniform_buffer_size = size_t(max_ubo_size);
|
||||
glGetInteger64v(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &max_ssbo_size);
|
||||
GCaps.max_storage_buffer_size = size_t(max_ssbo_size);
|
||||
GLint ssbo_alignment;
|
||||
glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &ssbo_alignment);
|
||||
GCaps.storage_buffer_alignment = size_t(ssbo_alignment);
|
||||
|
||||
GCaps.stencil_export_support = epoxy_has_gl_extension("GL_ARB_shader_stencil_export");
|
||||
|
||||
/* GL specific capabilities. */
|
||||
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GCaps.max_texture_3d_size);
|
||||
glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE,
|
||||
reinterpret_cast<int *>(&GCaps.max_buffer_texture_size));
|
||||
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GLContext::max_cubemap_size);
|
||||
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &GLContext::max_ubo_binds);
|
||||
GLint max_ssbo_binds;
|
||||
GLContext::max_ssbo_binds = 999999;
|
||||
glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &max_ssbo_binds);
|
||||
GLContext::max_ssbo_binds = min_ii(GLContext::max_ssbo_binds, max_ssbo_binds);
|
||||
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &max_ssbo_binds);
|
||||
GLContext::max_ssbo_binds = min_ii(GLContext::max_ssbo_binds, max_ssbo_binds);
|
||||
glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &max_ssbo_binds);
|
||||
GLContext::max_ssbo_binds = min_ii(GLContext::max_ssbo_binds, max_ssbo_binds);
|
||||
GLContext::debug_layer_support = epoxy_gl_version() >= 43 ||
|
||||
epoxy_has_gl_extension("GL_KHR_debug") ||
|
||||
epoxy_has_gl_extension("GL_ARB_debug_output");
|
||||
GLContext::direct_state_access_support = epoxy_has_gl_extension("GL_ARB_direct_state_access");
|
||||
GLContext::explicit_location_support = epoxy_gl_version() >= 43;
|
||||
GLContext::framebuffer_fetch_support = epoxy_has_gl_extension("GL_EXT_shader_framebuffer_fetch");
|
||||
GLContext::texture_barrier_support = epoxy_has_gl_extension("GL_ARB_texture_barrier");
|
||||
GLContext::layered_rendering_support = epoxy_has_gl_extension(
|
||||
"GL_ARB_shader_viewport_layer_array");
|
||||
GLContext::vertex_shader_viewport_index_support = epoxy_has_gl_extension(
|
||||
"GL_AMD_vertex_shader_viewport_index");
|
||||
GLContext::vertex_shader_layer_support = epoxy_has_gl_extension("GL_AMD_vertex_shader_layer");
|
||||
GLContext::native_barycentric_support = epoxy_has_gl_extension(
|
||||
"GL_AMD_shader_explicit_vertex_parameter");
|
||||
GLContext::multi_bind_support = GLContext::multi_bind_image_support = epoxy_has_gl_extension(
|
||||
"GL_ARB_multi_bind");
|
||||
GLContext::stencil_texturing_support = epoxy_gl_version() >= 43;
|
||||
GLContext::derivative_control_support = epoxy_gl_version() >= 45 ||
|
||||
epoxy_has_gl_extension("GL_ARB_derivative_control");
|
||||
GLContext::texture_filter_anisotropic_support = epoxy_has_gl_extension(
|
||||
"GL_EXT_texture_filter_anisotropic");
|
||||
|
||||
/* Disabled until it is proven to work. */
|
||||
GLContext::framebuffer_fetch_support = false;
|
||||
|
||||
detect_workarounds();
|
||||
|
||||
#if BLI_SUBPROCESS_SUPPORT
|
||||
GCaps.use_subprocess_shader_compilations = U.shader_compilation_method ==
|
||||
USER_SHADER_COMPILE_SUBPROCESS;
|
||||
#else
|
||||
GCaps.use_subprocess_shader_compilations = false;
|
||||
#endif
|
||||
if (G.debug & G_DEBUG_GPU_RENDERDOC) {
|
||||
/* Avoid crashes on RenderDoc sessions. */
|
||||
GCaps.use_subprocess_shader_compilations = false;
|
||||
}
|
||||
|
||||
int thread_count = U.gpu_shader_workers;
|
||||
|
||||
if (thread_count == 0) {
|
||||
/* Good default based on measurements. */
|
||||
|
||||
/* Always have at least 1 worker. */
|
||||
thread_count = 1;
|
||||
|
||||
if (GCaps.use_subprocess_shader_compilations) {
|
||||
/* Use reasonable number of worker by default when there are known gains. */
|
||||
if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) ||
|
||||
GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) ||
|
||||
GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY))
|
||||
{
|
||||
/* Subprocess is too costly in memory (>150MB per worker) to have better defaults. */
|
||||
thread_count = std::max(1, std::min(4, BLI_system_thread_count() / 2));
|
||||
}
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
||||
/* Best middle ground between memory usage and speedup as Nvidia context memory footprint
|
||||
* is quite heavy (~25MB). Moreover we have diminishing return after this because of PSO
|
||||
* compilation blocking the main thread.
|
||||
* Can be revisited if we find a way to delete the worker thread context after finishing
|
||||
* compilation, and fix the scheduling bubbles (#139775). */
|
||||
thread_count = 4;
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OPENSOURCE) ||
|
||||
GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_UNIX, GPU_DRIVER_ANY))
|
||||
{
|
||||
/* Mesa has very good compilation time and doesn't block the main thread.
|
||||
* The memory footprint of the worker context is rather small (<10MB).
|
||||
* Shader compilation gets much slower as the number of threads increases. */
|
||||
thread_count = 8;
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
||||
/* AMD proprietary driver's context have huge memory footprint (~45MB).
|
||||
* There is also not much gain from parallelization. */
|
||||
thread_count = 1;
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
/* Intel windows driver offer almost no speedup with parallel compilation. */
|
||||
thread_count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allow thread count override option to limit the number of workers and avoid allocating more
|
||||
* workers than needed. Also ensures that there is always 1 thread available for the UI. */
|
||||
int max_thread_count = std::max(1, BLI_system_thread_count() - 1);
|
||||
|
||||
GCaps.max_parallel_compilations = std::min(thread_count, max_thread_count);
|
||||
|
||||
/* Disable this feature entirely when not debugging. */
|
||||
if ((G.debug & G_DEBUG_GPU) == 0) {
|
||||
GLContext::debug_layer_support = false;
|
||||
GLContext::debug_layer_workaround = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Log extensions
|
||||
* \{ */
|
||||
|
||||
void GLBackend::log_extensions()
|
||||
{
|
||||
CLOG_DEBUG(&LOG,
|
||||
"OpenGL Extensions\n"
|
||||
" - [%c] Multi-bind\n"
|
||||
" - [%c] Direct state access\n"
|
||||
" - [%c] Anisotropic Texture Filtering\n"
|
||||
" - [%c] Layered rendering\n"
|
||||
" - [%c] Vertex shader viewport index\n"
|
||||
" - [%c] Vertex shader layer array\n"
|
||||
" - [%c] Native barycentric coordinates\n"
|
||||
" - [%c] Framebuffer fetch\n"
|
||||
" - [%c] Texture barrier\n"
|
||||
" - [%c] Shader stencil export\n"
|
||||
" - [%c] Derivative control\n",
|
||||
GLContext::multi_bind_support ? 'X' : ' ',
|
||||
GLContext::direct_state_access_support ? 'X' : ' ',
|
||||
GLContext::texture_filter_anisotropic_support ? 'X' : ' ',
|
||||
GLContext::layered_rendering_support ? 'X' : ' ',
|
||||
GLContext::vertex_shader_viewport_index_support ? 'X' : ' ',
|
||||
GLContext::vertex_shader_layer_support ? 'X' : ' ',
|
||||
GLContext::native_barycentric_support ? 'X' : ' ',
|
||||
GLContext::framebuffer_fetch_support ? 'X' : ' ',
|
||||
GLContext::texture_barrier_support ? 'X' : ' ',
|
||||
GCaps.stencil_export_support ? 'X' : ' ',
|
||||
GLContext::derivative_control_support ? 'X' : ' ');
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Log workarounds
|
||||
* \{ */
|
||||
|
||||
void GLBackend::log_workarounds()
|
||||
{
|
||||
CLOG_DEBUG(&LOG,
|
||||
"OpenGL Workarounds\n"
|
||||
" - [%c] Debug layer workaround\n"
|
||||
" - [%c] Generate mipmap workaround\n"
|
||||
" - [%c] Unused framebuffer slot workaround\n"
|
||||
" - [%c] Depth blitting workaround\n"
|
||||
" - [%c] Stencil classify buffer workaround\n"
|
||||
" - [%c] High-quality normals\n"
|
||||
" - [%c] Use main context\n",
|
||||
GLContext::debug_layer_workaround ? 'X' : ' ',
|
||||
GLContext::generate_mipmap_workaround ? 'X' : ' ',
|
||||
GLContext::unused_fb_slot_workaround ? 'X' : ' ',
|
||||
GCaps.depth_blitting_workaround ? 'X' : ' ',
|
||||
GCaps.stencil_clasify_buffer_workaround ? 'X' : ' ',
|
||||
GCaps.use_hq_normals_workaround ? 'X' : ' ',
|
||||
GCaps.use_main_context_workaround ? 'X' : ' ');
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
218
blender-5.2.0/source/blender/gpu/opengl/gl_backend.hh
Normal file
218
blender-5.2.0/source/blender/gpu/opengl/gl_backend.hh
Normal file
@@ -0,0 +1,218 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_capabilities_private.hh"
|
||||
|
||||
#ifdef WITH_RENDERDOC
|
||||
# include "renderdoc_api.hh"
|
||||
#endif
|
||||
|
||||
#include "gl_batch.hh"
|
||||
#include "gl_compilation_subprocess.hh"
|
||||
#include "gl_compute.hh"
|
||||
#include "gl_context.hh"
|
||||
#include "gl_framebuffer.hh"
|
||||
#include "gl_index_buffer.hh"
|
||||
#include "gl_query.hh"
|
||||
#include "gl_shader.hh"
|
||||
#include "gl_storage_buffer.hh"
|
||||
#include "gl_texture.hh"
|
||||
#include "gl_texture_pool.hh"
|
||||
#include "gl_uniform_buffer.hh"
|
||||
#include "gl_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLBackend : public GPUBackend {
|
||||
private:
|
||||
GLSharedOrphanLists shared_orphan_list_;
|
||||
#ifdef WITH_RENDERDOC
|
||||
renderdoc::api::Renderdoc renderdoc_;
|
||||
#endif
|
||||
|
||||
Set<int> valid_contexts_;
|
||||
std::mutex valid_contexts_mutex_;
|
||||
|
||||
public:
|
||||
GLBackend()
|
||||
{
|
||||
/* platform_init needs to go first. */
|
||||
GLBackend::platform_init();
|
||||
|
||||
GLBackend::capabilities_init();
|
||||
GLBackend::log_extensions();
|
||||
GLBackend::log_workarounds();
|
||||
GLTexture::samplers_init();
|
||||
}
|
||||
~GLBackend()
|
||||
{
|
||||
GLBackend::platform_exit();
|
||||
}
|
||||
|
||||
void init_resources() override
|
||||
{
|
||||
if (GCaps.use_subprocess_shader_compilations) {
|
||||
compiler_ = MEM_new<GLSubprocessShaderCompiler>(__func__);
|
||||
}
|
||||
else {
|
||||
compiler_ = MEM_new<GLShaderCompiler>(__func__);
|
||||
}
|
||||
};
|
||||
|
||||
void delete_resources() override
|
||||
{
|
||||
/* Delete any resources with context active. */
|
||||
GLTexture::samplers_free();
|
||||
MEM_delete(compiler_);
|
||||
}
|
||||
|
||||
static GLBackend *get()
|
||||
{
|
||||
return static_cast<GLBackend *>(GPUBackend::get());
|
||||
}
|
||||
|
||||
Context *context_alloc(GHOST_IWindow *ghost_window, GHOST_IContext * /*ghost_context*/) override
|
||||
{
|
||||
return new GLContext(ghost_window, shared_orphan_list_);
|
||||
};
|
||||
|
||||
void add_context_id(int context_id)
|
||||
{
|
||||
std::lock_guard lock(valid_contexts_mutex_);
|
||||
valid_contexts_.add(context_id);
|
||||
}
|
||||
|
||||
void remove_context_id(int context_id)
|
||||
{
|
||||
std::lock_guard lock(valid_contexts_mutex_);
|
||||
valid_contexts_.remove(context_id);
|
||||
}
|
||||
|
||||
bool is_valid_context_id(int context_id)
|
||||
{
|
||||
std::lock_guard lock(valid_contexts_mutex_);
|
||||
return valid_contexts_.contains(context_id);
|
||||
}
|
||||
|
||||
Batch *batch_alloc() override
|
||||
{
|
||||
return new GLBatch();
|
||||
};
|
||||
|
||||
Fence *fence_alloc() override
|
||||
{
|
||||
return new GLFence();
|
||||
};
|
||||
|
||||
FrameBuffer *framebuffer_alloc(const char *name) override
|
||||
{
|
||||
return new GLFrameBuffer(name);
|
||||
};
|
||||
|
||||
IndexBuf *indexbuf_alloc() override
|
||||
{
|
||||
return new GLIndexBuf();
|
||||
};
|
||||
|
||||
PixelBuffer *pixelbuf_alloc(size_t size) override
|
||||
{
|
||||
return new GLPixelBuffer(size);
|
||||
};
|
||||
|
||||
QueryPool *querypool_alloc() override
|
||||
{
|
||||
return new GLQueryPool();
|
||||
};
|
||||
|
||||
Shader *shader_alloc(const char *name) override
|
||||
{
|
||||
return new GLShader(name);
|
||||
};
|
||||
|
||||
Texture *texture_alloc(const char *name) override
|
||||
{
|
||||
return new GLTexture(name);
|
||||
};
|
||||
|
||||
TexturePool *texturepool_alloc() override;
|
||||
|
||||
UniformBuf *uniformbuf_alloc(size_t size, const char *name) override
|
||||
{
|
||||
return new GLUniformBuf(size, name);
|
||||
};
|
||||
|
||||
StorageBuf *storagebuf_alloc(size_t size, GPUUsageType usage, const char *name) override
|
||||
{
|
||||
return new GLStorageBuf(size, usage, name);
|
||||
};
|
||||
|
||||
VertBuf *vertbuf_alloc() override
|
||||
{
|
||||
return new GLVertBuf();
|
||||
};
|
||||
|
||||
GLSharedOrphanLists &shared_orphan_list_get()
|
||||
{
|
||||
return shared_orphan_list_;
|
||||
};
|
||||
|
||||
void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override
|
||||
{
|
||||
GLContext::get()->state_manager_active_get()->apply_state();
|
||||
GLCompute::dispatch(groups_x_len, groups_y_len, groups_z_len);
|
||||
}
|
||||
|
||||
void compute_dispatch_indirect(StorageBuf *indirect_buf) override
|
||||
{
|
||||
GLContext::get()->state_manager_active_get()->apply_state();
|
||||
|
||||
dynamic_cast<GLStorageBuf *>(indirect_buf)->bind_as(GL_DISPATCH_INDIRECT_BUFFER);
|
||||
/* This barrier needs to be here as it only work on the currently bound indirect buffer. */
|
||||
glMemoryBarrier(GL_COMMAND_BARRIER_BIT);
|
||||
|
||||
glDispatchComputeIndirect(GLintptr(0));
|
||||
/* Unbind. */
|
||||
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0);
|
||||
}
|
||||
|
||||
void shader_cache_dir_clear_old() override
|
||||
{
|
||||
#if BLI_SUBPROCESS_SUPPORT
|
||||
GL_shader_cache_dir_clear_old();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Render Frame Coordination */
|
||||
void render_begin() override {};
|
||||
void render_end() override {};
|
||||
void render_step(bool /*force_resource_release*/) override {};
|
||||
|
||||
bool debug_capture_begin(const char *title);
|
||||
void debug_capture_end();
|
||||
|
||||
private:
|
||||
static void platform_init();
|
||||
static void platform_exit();
|
||||
|
||||
static void capabilities_init();
|
||||
|
||||
static void log_extensions();
|
||||
static void log_workarounds();
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
321
blender-5.2.0/source/blender/gpu/opengl/gl_batch.cc
Normal file
321
blender-5.2.0/source/blender/gpu/opengl/gl_batch.cc
Normal file
@@ -0,0 +1,321 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GL implementation of #gpu::Batch.
|
||||
* The only specificity of GL here is that it caches a list of
|
||||
* Vertex Array Objects based on the bound shader interface.
|
||||
*/
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
#include "gl_context.hh"
|
||||
#include "gl_debug.hh"
|
||||
#include "gl_index_buffer.hh"
|
||||
#include "gl_primitive.hh"
|
||||
#include "gl_storage_buffer.hh"
|
||||
#include "gl_vertex_array.hh"
|
||||
|
||||
#include "gl_batch.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VAO Cache
|
||||
*
|
||||
* Each #GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration.
|
||||
* TODO(fclem): Could be revisited to avoid so much cross references.
|
||||
* \{ */
|
||||
|
||||
GLVaoCache::GLVaoCache()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
GLVaoCache::~GLVaoCache()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void GLVaoCache::init()
|
||||
{
|
||||
context_ = nullptr;
|
||||
interface_ = nullptr;
|
||||
is_dynamic_vao_count = false;
|
||||
for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
|
||||
static_vaos.interfaces[i] = nullptr;
|
||||
static_vaos.vao_ids[i] = 0;
|
||||
}
|
||||
vao_base_instance_ = 0;
|
||||
base_instance_ = 0;
|
||||
vao_id_ = 0;
|
||||
}
|
||||
|
||||
void GLVaoCache::insert(const GLShaderInterface *interface, GLuint vao)
|
||||
{
|
||||
/* Now insert the cache. */
|
||||
if (!is_dynamic_vao_count) {
|
||||
int i; /* find first unused slot */
|
||||
for (i = 0; i < GPU_VAO_STATIC_LEN; i++) {
|
||||
if (static_vaos.vao_ids[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < GPU_VAO_STATIC_LEN) {
|
||||
static_vaos.interfaces[i] = interface;
|
||||
static_vaos.vao_ids[i] = vao;
|
||||
}
|
||||
else {
|
||||
/* Erase previous entries, they will be added back if drawn again. */
|
||||
for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) {
|
||||
if (static_vaos.interfaces[i] != nullptr) {
|
||||
const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this);
|
||||
context_->vao_free(static_vaos.vao_ids[i]);
|
||||
}
|
||||
}
|
||||
/* Not enough place switch to dynamic. */
|
||||
is_dynamic_vao_count = true;
|
||||
/* Init dynamic arrays and let the branch below set the values. */
|
||||
dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT;
|
||||
dynamic_vaos.interfaces = MEM_new_array_zeroed<const GLShaderInterface *>(
|
||||
dynamic_vaos.count, "dyn vaos interfaces");
|
||||
dynamic_vaos.vao_ids = MEM_new_array_zeroed<GLuint>(dynamic_vaos.count, "dyn vaos ids");
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dynamic_vao_count) {
|
||||
int i; /* find first unused slot */
|
||||
for (i = 0; i < dynamic_vaos.count; i++) {
|
||||
if (dynamic_vaos.vao_ids[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == dynamic_vaos.count) {
|
||||
/* Not enough place, realloc the array. */
|
||||
i = dynamic_vaos.count;
|
||||
dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT;
|
||||
dynamic_vaos.interfaces = static_cast<const GLShaderInterface **>(MEM_realloc_zeroed(
|
||||
(void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * dynamic_vaos.count));
|
||||
dynamic_vaos.vao_ids = static_cast<GLuint *>(
|
||||
MEM_realloc_zeroed(dynamic_vaos.vao_ids, sizeof(GLuint) * dynamic_vaos.count));
|
||||
}
|
||||
dynamic_vaos.interfaces[i] = interface;
|
||||
dynamic_vaos.vao_ids[i] = vao;
|
||||
}
|
||||
|
||||
const_cast<GLShaderInterface *>(interface)->ref_add(this);
|
||||
}
|
||||
|
||||
void GLVaoCache::remove(const GLShaderInterface *interface)
|
||||
{
|
||||
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
|
||||
GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
|
||||
const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
|
||||
static_vaos.interfaces;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (interfaces[i] == interface) {
|
||||
context_->vao_free(vaos[i]);
|
||||
vaos[i] = 0;
|
||||
interfaces[i] = nullptr;
|
||||
break; /* cannot have duplicates */
|
||||
}
|
||||
}
|
||||
|
||||
if (interface_ == interface) {
|
||||
interface_ = nullptr;
|
||||
vao_id_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GLVaoCache::clear()
|
||||
{
|
||||
GLContext *ctx = GLContext::get();
|
||||
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
|
||||
GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids;
|
||||
const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
|
||||
static_vaos.interfaces;
|
||||
/* Early out, nothing to free. */
|
||||
if (context_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context_ == ctx) {
|
||||
glDeleteVertexArrays(count, vaos);
|
||||
glDeleteVertexArrays(1, &vao_base_instance_);
|
||||
}
|
||||
else {
|
||||
/* TODO(fclem): Slow way. Could avoid multiple mutex lock here */
|
||||
for (int i = 0; i < count; i++) {
|
||||
context_->vao_free(vaos[i]);
|
||||
}
|
||||
context_->vao_free(vao_base_instance_);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (interfaces[i] != nullptr) {
|
||||
const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dynamic_vao_count) {
|
||||
MEM_delete(dynamic_vaos.interfaces);
|
||||
MEM_delete(dynamic_vaos.vao_ids);
|
||||
}
|
||||
|
||||
if (context_) {
|
||||
context_->vao_cache_unregister(this);
|
||||
}
|
||||
/* Reinitialize. */
|
||||
this->init();
|
||||
}
|
||||
|
||||
GLuint GLVaoCache::lookup(const GLShaderInterface *interface)
|
||||
{
|
||||
const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN;
|
||||
const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces :
|
||||
static_vaos.interfaces;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (interfaces[i] == interface) {
|
||||
return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GLVaoCache::context_check()
|
||||
{
|
||||
GLContext *ctx = GLContext::get();
|
||||
BLI_assert(ctx);
|
||||
|
||||
if (context_ != ctx) {
|
||||
if (context_ != nullptr) {
|
||||
/* IMPORTANT: Trying to draw a batch in multiple different context will trash the VAO cache.
|
||||
* This has major performance impact and should be avoided in most cases. */
|
||||
context_->vao_cache_unregister(this);
|
||||
}
|
||||
this->clear();
|
||||
context_ = ctx;
|
||||
context_->vao_cache_register(this);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint GLVaoCache::vao_get(Batch *batch)
|
||||
{
|
||||
this->context_check();
|
||||
|
||||
Shader *shader = GLContext::get()->shader;
|
||||
GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface);
|
||||
if (interface_ != interface) {
|
||||
interface_ = interface;
|
||||
vao_id_ = this->lookup(interface_);
|
||||
|
||||
if (vao_id_ == 0) {
|
||||
/* Cache miss, create a new VAO. */
|
||||
glGenVertexArrays(1, &vao_id_);
|
||||
this->insert(interface_, vao_id_);
|
||||
GLVertArray::update_bindings(vao_id_, batch, interface_);
|
||||
}
|
||||
}
|
||||
|
||||
return vao_id_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Drawing
|
||||
* \{ */
|
||||
|
||||
void GLBatch::bind()
|
||||
{
|
||||
GLContext::get()->state_manager->apply_state();
|
||||
|
||||
if (flag & GPU_BATCH_DIRTY) {
|
||||
flag &= ~GPU_BATCH_DIRTY;
|
||||
vao_cache_.clear();
|
||||
}
|
||||
|
||||
glBindVertexArray(vao_cache_.vao_get(this));
|
||||
}
|
||||
|
||||
void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
|
||||
{
|
||||
GL_CHECK_RESOURCES("Batch");
|
||||
|
||||
this->bind();
|
||||
|
||||
BLI_assert(v_count > 0 && i_count > 0);
|
||||
|
||||
GLenum gl_type = to_gl(prim_type);
|
||||
|
||||
if (elem) {
|
||||
const GLIndexBuf *el = this->elem_();
|
||||
GLenum index_type = to_gl(el->index_type_);
|
||||
GLint base_index = el->index_base_;
|
||||
void *v_first_ofs = el->offset_ptr(v_first);
|
||||
|
||||
glDrawElementsInstancedBaseVertexBaseInstance(
|
||||
gl_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first);
|
||||
}
|
||||
else {
|
||||
glDrawArraysInstancedBaseInstance(gl_type, v_first, v_count, i_count, i_first);
|
||||
}
|
||||
}
|
||||
|
||||
void GLBatch::draw_indirect(gpu::StorageBuf *indirect_buf, intptr_t offset)
|
||||
{
|
||||
GL_CHECK_RESOURCES("Batch");
|
||||
|
||||
this->bind();
|
||||
dynamic_cast<GLStorageBuf *>(indirect_buf)->bind_as(GL_DRAW_INDIRECT_BUFFER);
|
||||
|
||||
GLenum gl_type = to_gl(prim_type);
|
||||
if (elem) {
|
||||
const GLIndexBuf *el = this->elem_();
|
||||
GLenum index_type = to_gl(el->index_type_);
|
||||
glDrawElementsIndirect(gl_type, index_type, reinterpret_cast<GLvoid *>(offset));
|
||||
}
|
||||
else {
|
||||
glDrawArraysIndirect(gl_type, reinterpret_cast<GLvoid *>(offset));
|
||||
}
|
||||
/* Unbind. */
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
|
||||
}
|
||||
|
||||
void GLBatch::multi_draw_indirect(gpu::StorageBuf *indirect_buf,
|
||||
int count,
|
||||
intptr_t offset,
|
||||
intptr_t stride)
|
||||
{
|
||||
GL_CHECK_RESOURCES("Batch");
|
||||
|
||||
this->bind();
|
||||
dynamic_cast<GLStorageBuf *>(indirect_buf)->bind_as(GL_DRAW_INDIRECT_BUFFER);
|
||||
|
||||
GLenum gl_type = to_gl(prim_type);
|
||||
if (elem) {
|
||||
const GLIndexBuf *el = this->elem_();
|
||||
GLenum index_type = to_gl(el->index_type_);
|
||||
glMultiDrawElementsIndirect(
|
||||
gl_type, index_type, reinterpret_cast<GLvoid *>(offset), count, stride);
|
||||
}
|
||||
else {
|
||||
glMultiDrawArraysIndirect(gl_type, reinterpret_cast<GLvoid *>(offset), count, stride);
|
||||
}
|
||||
/* Unbind. */
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
114
blender-5.2.0/source/blender/gpu/opengl/gl_batch.hh
Normal file
114
blender-5.2.0/source/blender/gpu/opengl/gl_batch.hh
Normal file
@@ -0,0 +1,114 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU geometry batch
|
||||
* Contains VAOs + VBOs + Shader representing a drawable entity.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
|
||||
#include "gl_index_buffer.hh"
|
||||
#include "gl_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLContext;
|
||||
class GLShaderInterface;
|
||||
|
||||
#define GPU_VAO_STATIC_LEN 3
|
||||
|
||||
/**
|
||||
* VAO management: remembers all geometry state (vertex attribute bindings & element buffer)
|
||||
* for each shader interface. Start with a static number of VAO's and fallback to dynamic count
|
||||
* if necessary. Once a batch goes dynamic it does not go back.
|
||||
*/
|
||||
class GLVaoCache {
|
||||
private:
|
||||
/** Context for which the vao_cache_ was generated. */
|
||||
GLContext *context_ = nullptr;
|
||||
/** Last interface this batch was drawn with. */
|
||||
GLShaderInterface *interface_ = nullptr;
|
||||
/** Cached VAO for the last interface. */
|
||||
GLuint vao_id_ = 0;
|
||||
/** Used when arb_base_instance is not supported. */
|
||||
GLuint vao_base_instance_ = 0;
|
||||
int base_instance_ = 0;
|
||||
|
||||
bool is_dynamic_vao_count = false;
|
||||
union {
|
||||
/** Static handle count */
|
||||
struct {
|
||||
const GLShaderInterface *interfaces[GPU_VAO_STATIC_LEN];
|
||||
GLuint vao_ids[GPU_VAO_STATIC_LEN];
|
||||
} static_vaos;
|
||||
/** Dynamic handle count */
|
||||
struct {
|
||||
uint count;
|
||||
const GLShaderInterface **interfaces;
|
||||
GLuint *vao_ids;
|
||||
} dynamic_vaos;
|
||||
};
|
||||
|
||||
public:
|
||||
GLVaoCache();
|
||||
~GLVaoCache();
|
||||
|
||||
GLuint vao_get(Batch *batch);
|
||||
|
||||
/**
|
||||
* Return 0 on cache miss (invalid VAO).
|
||||
*/
|
||||
GLuint lookup(const GLShaderInterface *interface);
|
||||
/**
|
||||
* Create a new VAO object and store it in the cache.
|
||||
*/
|
||||
void insert(const GLShaderInterface *interface, GLuint vao_id);
|
||||
void remove(const GLShaderInterface *interface);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
void init();
|
||||
/**
|
||||
* The #GLVaoCache object is only valid for one #GLContext.
|
||||
* Reset the cache if trying to draw in another context;.
|
||||
*/
|
||||
void context_check();
|
||||
};
|
||||
|
||||
class GLBatch : public Batch {
|
||||
public:
|
||||
/** All vaos corresponding to all the GPUShaderInterface this batch was drawn with. */
|
||||
GLVaoCache vao_cache_;
|
||||
|
||||
public:
|
||||
void draw(int v_first, int v_count, int i_first, int i_count) override;
|
||||
void draw_indirect(StorageBuf *indirect_buf, intptr_t offset) override;
|
||||
void multi_draw_indirect(StorageBuf *indirect_buf,
|
||||
int count,
|
||||
intptr_t offset,
|
||||
intptr_t stride) override;
|
||||
void bind();
|
||||
|
||||
/* Convenience getters. */
|
||||
|
||||
GLIndexBuf *elem_() const
|
||||
{
|
||||
return static_cast<GLIndexBuf *>(elem);
|
||||
}
|
||||
GLVertBuf *verts_(const int index) const
|
||||
{
|
||||
return static_cast<GLVertBuf *>(verts[index]);
|
||||
}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLBatch");
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
@@ -0,0 +1,335 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gl_compilation_subprocess.hh"
|
||||
|
||||
#if BLI_SUBPROCESS_SUPPORT
|
||||
|
||||
# include "BKE_appdir.hh"
|
||||
# include "BLI_fileops.hh"
|
||||
# include "BLI_hash.hh"
|
||||
# include "BLI_path_utils.hh"
|
||||
# include "BLI_string.h"
|
||||
# include "BLI_threads.h"
|
||||
# include "CLG_log.h"
|
||||
# include "GHOST_IContext.hh"
|
||||
# include "GHOST_ISystem.hh"
|
||||
# include "GPU_context.hh"
|
||||
# include "GPU_init_exit.hh"
|
||||
# include "gpu_capabilities_private.hh"
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
|
||||
# ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
# else
|
||||
# include "BLI_winstuff.h"
|
||||
# endif
|
||||
|
||||
/* Include after `BLI_winstuff.h` to avoid APIENTRY redefinition. */
|
||||
# include <epoxy/gl.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
|
||||
class SubprocessShader {
|
||||
GLuint comp_ = 0;
|
||||
GLuint vert_ = 0;
|
||||
GLuint geom_ = 0;
|
||||
GLuint frag_ = 0;
|
||||
GLuint program_ = 0;
|
||||
bool success_ = false;
|
||||
|
||||
public:
|
||||
SubprocessShader(const char *comp_src,
|
||||
const char *vert_src,
|
||||
const char *geom_src,
|
||||
const char *frag_src)
|
||||
{
|
||||
GLint status;
|
||||
program_ = glCreateProgram();
|
||||
|
||||
auto compile_stage = [&](const char *src, GLenum stage) -> GLuint {
|
||||
if (src == nullptr) {
|
||||
/* We only want status errors if compilation fails. */
|
||||
status = GL_TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLuint shader = glCreateShader(stage);
|
||||
glShaderSource(shader, 1, &src, nullptr);
|
||||
glCompileShader(shader);
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
glAttachShader(program_, shader);
|
||||
return shader;
|
||||
};
|
||||
|
||||
comp_ = compile_stage(comp_src, GL_COMPUTE_SHADER);
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
vert_ = compile_stage(vert_src, GL_VERTEX_SHADER);
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
geom_ = compile_stage(geom_src, GL_GEOMETRY_SHADER);
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
frag_ = compile_stage(frag_src, GL_FRAGMENT_SHADER);
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
glLinkProgram(program_);
|
||||
glGetProgramiv(program_, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
return;
|
||||
}
|
||||
|
||||
success_ = true;
|
||||
}
|
||||
|
||||
~SubprocessShader()
|
||||
{
|
||||
glDeleteShader(comp_);
|
||||
glDeleteShader(vert_);
|
||||
glDeleteShader(geom_);
|
||||
glDeleteShader(frag_);
|
||||
glDeleteProgram(program_);
|
||||
}
|
||||
|
||||
ShaderBinaryHeader *get_binary(void *memory)
|
||||
{
|
||||
ShaderBinaryHeader *bin = reinterpret_cast<ShaderBinaryHeader *>(memory);
|
||||
bin->format = 0;
|
||||
bin->size = 0;
|
||||
|
||||
if (success_) {
|
||||
glGetProgramiv(program_, GL_PROGRAM_BINARY_LENGTH, &bin->size);
|
||||
if (bin->size > sizeof(ShaderBinaryHeader::data)) {
|
||||
bin->size = 0;
|
||||
return nullptr;
|
||||
}
|
||||
glGetProgramBinary(program_, bin->size, nullptr, &bin->format, bin->data);
|
||||
}
|
||||
|
||||
return bin;
|
||||
}
|
||||
};
|
||||
|
||||
/* Check if the binary is valid and can be loaded by the driver. */
|
||||
static bool validate_binary(void *binary)
|
||||
{
|
||||
ShaderBinaryHeader *bin = reinterpret_cast<ShaderBinaryHeader *>(binary);
|
||||
GLuint program = glCreateProgram();
|
||||
glProgramBinary(program, bin->format, bin->data, bin->size);
|
||||
GLint status;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
||||
glDeleteProgram(program);
|
||||
return status;
|
||||
}
|
||||
|
||||
std::string GL_shader_cache_dir_get()
|
||||
{
|
||||
static char tmp_dir_buffer[1024];
|
||||
BKE_appdir_folder_caches(tmp_dir_buffer, sizeof(tmp_dir_buffer));
|
||||
|
||||
std::string cache_dir = std::string(tmp_dir_buffer) + "gl-shader-cache" + SEP_STR;
|
||||
BLI_dir_create_recursive(cache_dir.c_str());
|
||||
|
||||
return cache_dir;
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
void GPU_compilation_subprocess_run(const char *subprocess_name)
|
||||
{
|
||||
using namespace blender::gpu;
|
||||
|
||||
# ifndef _WIN32
|
||||
/** NOTE: Technically, the parent process could have crashed before this. */
|
||||
pid_t ppid = getppid();
|
||||
# endif
|
||||
|
||||
CLG_init();
|
||||
BLI_threadapi_init();
|
||||
|
||||
/* Prevent the ShaderCompiler from spawning extra threads/contexts, we don't need them. */
|
||||
GCaps.use_main_context_workaround = true;
|
||||
|
||||
std::string name = subprocess_name;
|
||||
SharedMemory shared_mem(name, compilation_subprocess_shared_memory_size, false);
|
||||
if (!shared_mem.get_data()) {
|
||||
std::cerr << "Compilation Subprocess: Failed to open shared memory " << subprocess_name
|
||||
<< "\n";
|
||||
return;
|
||||
}
|
||||
SharedSemaphore start_semaphore(name + "_START", true);
|
||||
SharedSemaphore end_semaphore(name + "_END", true);
|
||||
SharedSemaphore close_semaphore(name + "_CLOSE", true);
|
||||
|
||||
GHOST_ISystem::createSystemBackground();
|
||||
GHOST_ISystem *ghost_system = GHOST_ISystem::getSystem();
|
||||
BLI_assert(ghost_system);
|
||||
GPU_backend_ghost_system_set(ghost_system);
|
||||
GHOST_GPUSettings gpu_settings = {0};
|
||||
gpu_settings.context_type = GHOST_kDrawingContextTypeOpenGL;
|
||||
GHOST_IContext *ghost_context = ghost_system->createOffscreenContext(gpu_settings);
|
||||
if (ghost_context == nullptr) {
|
||||
std::cerr << "Compilation Subprocess: Failed to initialize GHOST context for "
|
||||
<< subprocess_name << "\n";
|
||||
GHOST_ISystem::disposeSystem();
|
||||
return;
|
||||
}
|
||||
ghost_context->activateDrawingContext();
|
||||
GPUContext *gpu_context = GPU_context_create(nullptr, ghost_context);
|
||||
GPU_init();
|
||||
|
||||
std::string cache_dir = GL_shader_cache_dir_get();
|
||||
|
||||
while (true) {
|
||||
/* Process events to avoid crashes on Wayland.
|
||||
* See https://bugreports.qt.io/browse/QTBUG-81504 */
|
||||
ghost_system->processEvents(false);
|
||||
|
||||
# ifdef _WIN32
|
||||
start_semaphore.decrement();
|
||||
# else
|
||||
bool lost_parent = false;
|
||||
while (!lost_parent && !start_semaphore.try_decrement(1000)) {
|
||||
lost_parent = getppid() != ppid;
|
||||
}
|
||||
if (lost_parent) {
|
||||
std::cerr << "Compilation Subprocess: Lost parent process\n";
|
||||
break;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (close_semaphore.try_decrement()) {
|
||||
break;
|
||||
}
|
||||
|
||||
ShaderSourceHeader *source = reinterpret_cast<ShaderSourceHeader *>(shared_mem.get_data());
|
||||
const char *next_src = source->sources;
|
||||
const char *comp_src = nullptr;
|
||||
const char *vert_src = nullptr;
|
||||
const char *geom_src = nullptr;
|
||||
const char *frag_src = nullptr;
|
||||
|
||||
DefaultHash<StringRefNull> hasher;
|
||||
std::string hash_str = "_";
|
||||
|
||||
auto get_src = [&]() {
|
||||
const char *src = next_src;
|
||||
next_src += strlen(src) + sizeof('\0');
|
||||
hash_str += std::to_string(hasher(src)) + "_";
|
||||
return src;
|
||||
};
|
||||
|
||||
if (source->type == ShaderSourceHeader::Type::COMPUTE) {
|
||||
comp_src = get_src();
|
||||
}
|
||||
else {
|
||||
vert_src = get_src();
|
||||
if (source->type == ShaderSourceHeader::Type::GRAPHICS_WITH_GEOMETRY_STAGE) {
|
||||
geom_src = get_src();
|
||||
}
|
||||
frag_src = get_src();
|
||||
}
|
||||
|
||||
std::string cache_path = cache_dir + SEP_STR + hash_str;
|
||||
|
||||
/* TODO: This should lock the files? */
|
||||
if (BLI_exists(cache_path.c_str())) {
|
||||
{
|
||||
/* Store the source hash in the shared memory.
|
||||
* If the subprocess crashes, the main process will delete the cache file. */
|
||||
std::string source_hash = "SOURCE_HASH:" + hash_str;
|
||||
BLI_strncpy(reinterpret_cast<char *>(shared_mem.get_data()),
|
||||
source_hash.c_str(),
|
||||
source_hash.size() + 1);
|
||||
}
|
||||
/* Prevent old cache files from being deleted if they're still being used. */
|
||||
BLI_file_touch(cache_path.c_str());
|
||||
/* Read cached binary. */
|
||||
fstream file(cache_path, std::ios::binary | std::ios::in | std::ios::ate);
|
||||
std::streamsize size = file.tellg();
|
||||
if (size <= compilation_subprocess_shared_memory_size) {
|
||||
file.seekg(0, std::ios::beg);
|
||||
/* Use temp memory so we don't overwrite the source hash. */
|
||||
static char tmp_mem[compilation_subprocess_shared_memory_size];
|
||||
file.read(tmp_mem, size);
|
||||
/* Close first in case validation hangs the driver. */
|
||||
file.close();
|
||||
/* Ensure it's valid. */
|
||||
if (!validate_binary(tmp_mem)) {
|
||||
std::cout << "Compilation Subprocess: Failed to load cached shader binary " << hash_str
|
||||
<< "\n";
|
||||
/* TODO: No longer true. */
|
||||
/* We can't compile the shader anymore since we have written over the source code,
|
||||
* but we delete the cache for the next time this shader is requested. */
|
||||
BLI_delete(cache_path.c_str(), false, false);
|
||||
}
|
||||
/* Copy the temp memory to the shared memory now that we know loading the shader doesn't
|
||||
* crash the driver. */
|
||||
memcpy(shared_mem.get_data(), tmp_mem, size);
|
||||
end_semaphore.increment();
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
/* This should never happen, since shaders larger than the pool size should be discarded
|
||||
* and compiled in the main Blender process. */
|
||||
std::cerr << "Compilation Subprocess: Wrong size for cached shader binary " << hash_str
|
||||
<< "\n";
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
SubprocessShader shader(comp_src, vert_src, geom_src, frag_src);
|
||||
ShaderBinaryHeader *binary = shader.get_binary(shared_mem.get_data());
|
||||
|
||||
if (binary) {
|
||||
fstream file(cache_path, std::ios::binary | std::ios::out);
|
||||
file.write(reinterpret_cast<char *>(shared_mem.get_data()),
|
||||
binary->size + offsetof(ShaderBinaryHeader, data));
|
||||
}
|
||||
|
||||
end_semaphore.increment();
|
||||
}
|
||||
|
||||
GPU_exit();
|
||||
GPU_context_discard(gpu_context);
|
||||
ghost_system->disposeContext(ghost_context);
|
||||
GHOST_ISystem::disposeSystem();
|
||||
}
|
||||
|
||||
namespace gpu {
|
||||
void GL_shader_cache_dir_clear_old()
|
||||
{
|
||||
std::string cache_dir = GL_shader_cache_dir_get();
|
||||
|
||||
direntry *entries = nullptr;
|
||||
uint32_t dir_len = BLI_filelist_dir_contents(cache_dir.c_str(), &entries);
|
||||
for (int i : IndexRange(dir_len)) {
|
||||
direntry entry = entries[i];
|
||||
if (S_ISDIR(entry.s.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
const time_t ts_now = time(nullptr);
|
||||
const time_t delete_threshold = 60 /*seconds*/ * 60 /*minutes*/ * 24 /*hours*/ * 30 /*days*/;
|
||||
if (entry.s.st_mtime + delete_threshold < ts_now) {
|
||||
BLI_delete(entry.path, false, false);
|
||||
}
|
||||
}
|
||||
BLI_filelist_free(entries, dir_len);
|
||||
}
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_compilation_subprocess.hh"
|
||||
|
||||
#if BLI_SUBPROCESS_SUPPORT
|
||||
|
||||
# include "BLI_sys_types.h"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* The size of the memory pools shared by Blender and the compilation subprocesses. */
|
||||
constexpr size_t compilation_subprocess_shared_memory_size = 1024 * 1024 * 5; /* 5 MiB */
|
||||
|
||||
struct ShaderSourceHeader {
|
||||
enum Type { COMPUTE, GRAPHICS, GRAPHICS_WITH_GEOMETRY_STAGE };
|
||||
/* The type of program being compiled. */
|
||||
Type type;
|
||||
/* The source code for all the shader stages (Separated by a null terminator).
|
||||
* The stages follows the execution order (eg. vert > geom > frag). */
|
||||
char sources[compilation_subprocess_shared_memory_size - sizeof(type)];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderSourceHeader) == compilation_subprocess_shared_memory_size,
|
||||
"Size must match the shared memory size");
|
||||
|
||||
struct ShaderBinaryHeader {
|
||||
/* Size of the shader binary data. */
|
||||
int32_t size;
|
||||
/* Magic number that identifies the format of this shader binary (Driver-defined).
|
||||
* This (and size) is set to 0 when the shader has failed to compile. */
|
||||
uint32_t format;
|
||||
/* The serialized shader binary data. */
|
||||
uint8_t data[compilation_subprocess_shared_memory_size - sizeof(size) - sizeof(format)];
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShaderBinaryHeader) == compilation_subprocess_shared_memory_size,
|
||||
"Size must match the shared memory size");
|
||||
|
||||
void GL_shader_cache_dir_clear_old();
|
||||
|
||||
std::string GL_shader_cache_dir_get();
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
||||
#endif
|
||||
28
blender-5.2.0/source/blender/gpu/opengl/gl_compute.cc
Normal file
28
blender-5.2.0/source/blender/gpu/opengl/gl_compute.cc
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "gl_compute.hh"
|
||||
|
||||
#include "gl_debug.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len)
|
||||
{
|
||||
GL_CHECK_RESOURCES("Compute");
|
||||
|
||||
/* Sometime we reference a dispatch size but we want to skip it by setting one dimension to 0.
|
||||
* Avoid error being reported on some implementation for these case. */
|
||||
if (group_x_len == 0 || group_y_len == 0 || group_z_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
glDispatchCompute(group_x_len, group_y_len, group_z_len);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
18
blender-5.2.0/source/blender/gpu/opengl/gl_compute.hh
Normal file
18
blender-5.2.0/source/blender/gpu/opengl/gl_compute.hh
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLCompute {
|
||||
public:
|
||||
static void dispatch(int group_x_len, int group_y_len, int group_z_len);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
379
blender-5.2.0/source/blender/gpu/opengl/gl_context.cc
Normal file
379
blender-5.2.0/source/blender/gpu/opengl/gl_context.cc
Normal file
@@ -0,0 +1,379 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "GPU_framebuffer.hh"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_immediate_private.hh"
|
||||
|
||||
#include "gl_debug.hh"
|
||||
#include "gl_immediate.hh"
|
||||
#include "gl_state.hh"
|
||||
#include "gl_uniform_buffer.hh"
|
||||
|
||||
#include "gl_backend.hh" /* TODO: remove. */
|
||||
#include "gl_context.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Constructor / Destructor
|
||||
* \{ */
|
||||
|
||||
GLContext::GLContext(GHOST_IWindow *ghost_window, GLSharedOrphanLists &shared_orphan_list)
|
||||
: shared_orphan_list_(shared_orphan_list)
|
||||
{
|
||||
GLBackend::get()->add_context_id(context_id);
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
debug::init_gl_callbacks();
|
||||
}
|
||||
|
||||
float data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
glGenBuffers(1, &default_attr_vbo_);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, default_attr_vbo_);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
state_manager = new GLStateManager();
|
||||
imm = new GLImmediate();
|
||||
ghost_window_ = ghost_window;
|
||||
|
||||
if (ghost_window) {
|
||||
GLuint default_fbo = ghost_window->getDefaultFramebuffer();
|
||||
|
||||
GHOST_Rect bounds;
|
||||
ghost_window->getClientBounds(bounds);
|
||||
const int w = bounds.getWidth();
|
||||
const int h = bounds.getHeight();
|
||||
|
||||
if (default_fbo != 0) {
|
||||
/* Bind default framebuffer, otherwise state might be undefined. */
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, default_fbo);
|
||||
front_left = new GLFrameBuffer("front_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h);
|
||||
back_left = new GLFrameBuffer("back_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h);
|
||||
}
|
||||
else {
|
||||
front_left = new GLFrameBuffer("front_left", this, GL_FRONT_LEFT, 0, w, h);
|
||||
back_left = new GLFrameBuffer("back_left", this, GL_BACK_LEFT, 0, w, h);
|
||||
}
|
||||
|
||||
GLboolean supports_stereo_quad_buffer = GL_FALSE;
|
||||
glGetBooleanv(GL_STEREO, &supports_stereo_quad_buffer);
|
||||
if (supports_stereo_quad_buffer) {
|
||||
front_right = new GLFrameBuffer("front_right", this, GL_FRONT_RIGHT, 0, w, h);
|
||||
back_right = new GLFrameBuffer("back_right", this, GL_BACK_RIGHT, 0, w, h);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* For off-screen contexts. Default frame-buffer is null. */
|
||||
back_left = new GLFrameBuffer("back_left", this, GL_NONE, 0, 0, 0);
|
||||
}
|
||||
|
||||
active_fb = back_left;
|
||||
static_cast<GLStateManager *>(state_manager)->active_fb = static_cast<GLFrameBuffer *>(
|
||||
active_fb);
|
||||
}
|
||||
|
||||
GLContext::~GLContext()
|
||||
{
|
||||
if (G.profile_gpu) {
|
||||
/* Ensure query results are available. */
|
||||
finish();
|
||||
process_frame_timings();
|
||||
}
|
||||
free_resources();
|
||||
BLI_assert(orphaned_framebuffers_.is_empty());
|
||||
BLI_assert(orphaned_vertarrays_.is_empty());
|
||||
/* For now don't allow GPUFrameBuffers to be reuse in another context. */
|
||||
BLI_assert(framebuffers_.is_empty());
|
||||
/* Delete VAO's so the batch can be reused in another context. */
|
||||
for (GLVaoCache *cache : vao_caches_) {
|
||||
cache->clear();
|
||||
}
|
||||
glDeleteBuffers(1, &default_attr_vbo_);
|
||||
|
||||
GLBackend::get()->remove_context_id(context_id);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Activate / Deactivate context
|
||||
* \{ */
|
||||
|
||||
void GLContext::activate()
|
||||
{
|
||||
/* Make sure no other context is already bound to this thread. */
|
||||
BLI_assert(is_active_ == false);
|
||||
|
||||
is_active_ = true;
|
||||
thread_ = pthread_self();
|
||||
|
||||
/* Clear accumulated orphans. */
|
||||
orphans_clear();
|
||||
|
||||
if (ghost_window_) {
|
||||
/* Get the correct framebuffer size for the internal framebuffers. */
|
||||
GHOST_Rect bounds = {0};
|
||||
ghost_window_->getClientBounds(bounds);
|
||||
|
||||
const int w = bounds.getWidth();
|
||||
const int h = bounds.getHeight();
|
||||
|
||||
if (front_left) {
|
||||
front_left->size_set(w, h);
|
||||
}
|
||||
if (back_left) {
|
||||
back_left->size_set(w, h);
|
||||
}
|
||||
if (front_right) {
|
||||
front_right->size_set(w, h);
|
||||
}
|
||||
if (back_right) {
|
||||
back_right->size_set(w, h);
|
||||
}
|
||||
}
|
||||
|
||||
/* Not really following the state but we should consider
|
||||
* no ubo bound when activating a context. */
|
||||
bound_ubo_slots = 0;
|
||||
bound_ssbo_slots = 0;
|
||||
|
||||
immActivate();
|
||||
}
|
||||
|
||||
void GLContext::deactivate()
|
||||
{
|
||||
immDeactivate();
|
||||
is_active_ = false;
|
||||
}
|
||||
|
||||
void GLContext::begin_frame()
|
||||
{
|
||||
/* No-op. */
|
||||
}
|
||||
|
||||
void GLContext::end_frame()
|
||||
{
|
||||
process_frame_timings();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Flush, Finish & sync
|
||||
* \{ */
|
||||
|
||||
void GLContext::flush()
|
||||
{
|
||||
glFlush();
|
||||
}
|
||||
|
||||
void GLContext::finish()
|
||||
{
|
||||
glFinish();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Safe object deletion
|
||||
*
|
||||
* GPU objects can be freed when the context is not bound.
|
||||
* In this case we delay the deletion until the context is bound again.
|
||||
* \{ */
|
||||
|
||||
void GLSharedOrphanLists::OrphanList::clear(FunctionRef<void(GLuint, GLuint *)> free_fn)
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
if (!handles_.is_empty()) {
|
||||
free_fn(uint(handles_.size()), handles_.data());
|
||||
handles_.clear();
|
||||
}
|
||||
};
|
||||
|
||||
void GLSharedOrphanLists::OrphanList::append(GLuint handle)
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
handles_.append(handle);
|
||||
};
|
||||
|
||||
void GLSharedOrphanLists::orphans_clear()
|
||||
{
|
||||
/* Check if any context is active on this thread! */
|
||||
BLI_assert(GLContext::get());
|
||||
|
||||
buffers.clear(glDeleteBuffers);
|
||||
textures.clear(glDeleteTextures);
|
||||
shaders.clear([](GLuint size, GLuint *handles) {
|
||||
for (uint i = 0; i < size; i++) {
|
||||
glDeleteShader(handles[i]);
|
||||
}
|
||||
});
|
||||
programs.clear([](GLuint size, GLuint *handles) {
|
||||
for (uint i = 0; i < size; i++) {
|
||||
glDeleteProgram(handles[i]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
void GLContext::orphans_clear()
|
||||
{
|
||||
/* Check if context has been activated by another thread! */
|
||||
BLI_assert(this->is_active_on_thread());
|
||||
|
||||
lists_mutex_.lock();
|
||||
if (!orphaned_vertarrays_.is_empty()) {
|
||||
glDeleteVertexArrays(uint(orphaned_vertarrays_.size()), orphaned_vertarrays_.data());
|
||||
orphaned_vertarrays_.clear();
|
||||
}
|
||||
if (!orphaned_framebuffers_.is_empty()) {
|
||||
glDeleteFramebuffers(uint(orphaned_framebuffers_.size()), orphaned_framebuffers_.data());
|
||||
orphaned_framebuffers_.clear();
|
||||
}
|
||||
lists_mutex_.unlock();
|
||||
|
||||
shared_orphan_list_.orphans_clear();
|
||||
};
|
||||
|
||||
void GLContext::orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, GLuint id)
|
||||
{
|
||||
list_mutex.lock();
|
||||
orphan_list.append(id);
|
||||
list_mutex.unlock();
|
||||
}
|
||||
|
||||
void GLContext::vao_free(GLuint vao_id)
|
||||
{
|
||||
if (this == GLContext::get()) {
|
||||
glDeleteVertexArrays(1, &vao_id);
|
||||
}
|
||||
else {
|
||||
orphans_add(orphaned_vertarrays_, lists_mutex_, vao_id);
|
||||
}
|
||||
}
|
||||
|
||||
void GLContext::fbo_free(GLuint fbo_id)
|
||||
{
|
||||
if (this == GLContext::get()) {
|
||||
glDeleteFramebuffers(1, &fbo_id);
|
||||
}
|
||||
else {
|
||||
orphans_add(orphaned_framebuffers_, lists_mutex_, fbo_id);
|
||||
}
|
||||
}
|
||||
|
||||
void GLContext::buffer_free(GLuint buf_id)
|
||||
{
|
||||
/* Any context can free. */
|
||||
if (GLContext::get()) {
|
||||
glDeleteBuffers(1, &buf_id);
|
||||
}
|
||||
else {
|
||||
GLSharedOrphanLists &orphan_list = GLBackend::get()->shared_orphan_list_get();
|
||||
orphan_list.buffers.append(buf_id);
|
||||
}
|
||||
}
|
||||
|
||||
void GLContext::texture_free(GLuint tex_id)
|
||||
{
|
||||
/* Any context can free. */
|
||||
if (GLContext::get()) {
|
||||
glDeleteTextures(1, &tex_id);
|
||||
}
|
||||
else {
|
||||
GLSharedOrphanLists &orphan_list = GLBackend::get()->shared_orphan_list_get();
|
||||
orphan_list.textures.append(tex_id);
|
||||
}
|
||||
}
|
||||
|
||||
void GLContext::shader_free(GLuint shader_id)
|
||||
{
|
||||
/* Any context can free. */
|
||||
if (GLContext::get()) {
|
||||
glDeleteShader(shader_id);
|
||||
}
|
||||
else {
|
||||
GLSharedOrphanLists &orphan_list = GLBackend::get()->shared_orphan_list_get();
|
||||
orphan_list.shaders.append(shader_id);
|
||||
}
|
||||
}
|
||||
|
||||
void GLContext::program_free(GLuint program_id)
|
||||
{
|
||||
/* Any context can free. */
|
||||
if (GLContext::get()) {
|
||||
glDeleteProgram(program_id);
|
||||
}
|
||||
else {
|
||||
GLSharedOrphanLists &orphan_list = GLBackend::get()->shared_orphan_list_get();
|
||||
orphan_list.programs.append(program_id);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Linked object deletion
|
||||
*
|
||||
* These objects contain data that are stored per context. We
|
||||
* need to do some cleanup if they are used across context or if context
|
||||
* is discarded.
|
||||
* \{ */
|
||||
|
||||
void GLContext::vao_cache_register(GLVaoCache *cache)
|
||||
{
|
||||
lists_mutex_.lock();
|
||||
vao_caches_.add(cache);
|
||||
lists_mutex_.unlock();
|
||||
}
|
||||
|
||||
void GLContext::vao_cache_unregister(GLVaoCache *cache)
|
||||
{
|
||||
lists_mutex_.lock();
|
||||
vao_caches_.remove(cache);
|
||||
lists_mutex_.unlock();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Memory statistics
|
||||
* \{ */
|
||||
|
||||
void GLContext::memory_statistics_get(int *r_total_mem, int *r_free_mem)
|
||||
{
|
||||
if (epoxy_has_gl_extension("GL_NVX_gpu_memory_info")) {
|
||||
/* Returned value in Kb. */
|
||||
glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, r_total_mem);
|
||||
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, r_free_mem);
|
||||
}
|
||||
else if (epoxy_has_gl_extension("GL_ATI_meminfo")) {
|
||||
int stats[4];
|
||||
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, stats);
|
||||
|
||||
*r_total_mem = 0;
|
||||
*r_free_mem = stats[0]; /* Total memory free in the pool. */
|
||||
}
|
||||
else {
|
||||
*r_total_mem = 0;
|
||||
*r_free_mem = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
182
blender-5.2.0/source/blender/gpu/opengl/gl_context.hh
Normal file
182
blender-5.2.0/source/blender/gpu/opengl/gl_context.hh
Normal file
@@ -0,0 +1,182 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "GPU_framebuffer.hh"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gl_state.hh"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLVaoCache;
|
||||
|
||||
class GLSharedOrphanLists {
|
||||
class OrphanList {
|
||||
/** Mutex for the below structures. */
|
||||
std::mutex mutex_;
|
||||
/** Buffers and textures are shared across context. Any context can free them. */
|
||||
Vector<GLuint> handles_;
|
||||
|
||||
public:
|
||||
void clear(FunctionRef<void(GLuint, GLuint *)> free_fn);
|
||||
void append(GLuint handle);
|
||||
};
|
||||
|
||||
public:
|
||||
/** Shaders, Buffers and textures are shared across context. */
|
||||
OrphanList textures;
|
||||
OrphanList buffers;
|
||||
OrphanList shaders;
|
||||
OrphanList programs;
|
||||
|
||||
void orphans_clear();
|
||||
};
|
||||
|
||||
class GLContext : public Context {
|
||||
public:
|
||||
/** Capabilities. */
|
||||
|
||||
static GLint max_cubemap_size;
|
||||
static GLint max_ubo_binds;
|
||||
static GLint max_ssbo_binds;
|
||||
|
||||
/** Extensions. */
|
||||
|
||||
static bool debug_layer_support;
|
||||
static bool direct_state_access_support;
|
||||
static bool explicit_location_support;
|
||||
static bool framebuffer_fetch_support;
|
||||
/* layered_rendering_support requires GL_ARB_shader_viewport_layer_array, which is a superset of
|
||||
* GL_AMD_vertex_shader_viewport_index (vertex_shader_viewport_index_support) and
|
||||
* GL_AMD_vertex_shader_layer (vertex_shader_layer_support) with additional support for
|
||||
* tessellation evaluation shaders (which are not used by the GPU module). */
|
||||
static bool layered_rendering_support;
|
||||
static bool vertex_shader_viewport_index_support;
|
||||
static bool vertex_shader_layer_support;
|
||||
static bool native_barycentric_support;
|
||||
static bool multi_bind_support;
|
||||
static bool multi_bind_image_support;
|
||||
static bool stencil_texturing_support;
|
||||
static bool texture_barrier_support;
|
||||
static bool texture_filter_anisotropic_support;
|
||||
static bool derivative_control_support;
|
||||
|
||||
/** Workarounds. */
|
||||
|
||||
static bool debug_layer_workaround;
|
||||
static bool unused_fb_slot_workaround;
|
||||
static bool generate_mipmap_workaround;
|
||||
|
||||
/** VBO for missing vertex attribute binding. Avoid undefined behavior on some implementation. */
|
||||
GLuint default_attr_vbo_;
|
||||
|
||||
/** Used for debugging purpose. Bit-flags of all bound slots. */
|
||||
uint16_t bound_ubo_slots;
|
||||
uint16_t bound_ssbo_slots;
|
||||
|
||||
private:
|
||||
/**
|
||||
* #Batch & #GPUFramebuffer have references to the context they are from, in the case the
|
||||
* context is destroyed, we need to remove any reference to it.
|
||||
*/
|
||||
Set<GLVaoCache *> vao_caches_;
|
||||
Set<gpu::FrameBuffer *> framebuffers_;
|
||||
/** Mutex for the below structures. */
|
||||
std::mutex lists_mutex_;
|
||||
/** VertexArrays and framebuffers are not shared across context. */
|
||||
Vector<GLuint> orphaned_vertarrays_;
|
||||
Vector<GLuint> orphaned_framebuffers_;
|
||||
/** #GLBackend owns this data. */
|
||||
GLSharedOrphanLists &shared_orphan_list_;
|
||||
|
||||
struct TimeQuery {
|
||||
std::string name;
|
||||
union {
|
||||
GLuint handles[2];
|
||||
struct {
|
||||
GLuint handle_start, handle_end;
|
||||
};
|
||||
};
|
||||
bool finished;
|
||||
int64_t cpu_start;
|
||||
int64_t cpu_end;
|
||||
};
|
||||
struct FrameQueries {
|
||||
Vector<TimeQuery> queries;
|
||||
};
|
||||
Vector<FrameQueries> frame_timings;
|
||||
|
||||
void process_frame_timings();
|
||||
|
||||
public:
|
||||
GLContext(GHOST_IWindow *ghost_window, GLSharedOrphanLists &shared_orphan_list);
|
||||
~GLContext();
|
||||
|
||||
static void check_error(const char *info);
|
||||
|
||||
void activate() override;
|
||||
void deactivate() override;
|
||||
void begin_frame() override;
|
||||
void end_frame() override;
|
||||
|
||||
void flush() override;
|
||||
void finish() override;
|
||||
|
||||
void memory_statistics_get(int *r_total_mem, int *r_free_mem) override;
|
||||
|
||||
static GLContext *get()
|
||||
{
|
||||
return static_cast<GLContext *>(Context::get());
|
||||
}
|
||||
|
||||
static GLStateManager *state_manager_active_get()
|
||||
{
|
||||
GLContext *ctx = GLContext::get();
|
||||
return static_cast<GLStateManager *>(ctx->state_manager);
|
||||
};
|
||||
|
||||
/* These need to be called with the context the id was created with. */
|
||||
void vao_free(GLuint vao_id);
|
||||
void fbo_free(GLuint fbo_id);
|
||||
/* These can be called by any threads even without OpenGL ctx. Deletion will be delayed. */
|
||||
static void buffer_free(GLuint buf_id);
|
||||
static void texture_free(GLuint tex_id);
|
||||
static void shader_free(GLuint shader_id);
|
||||
static void program_free(GLuint program_id);
|
||||
|
||||
void vao_cache_register(GLVaoCache *cache);
|
||||
void vao_cache_unregister(GLVaoCache *cache);
|
||||
|
||||
void debug_group_begin(const char *name, int index) override;
|
||||
void debug_group_end() override;
|
||||
bool debug_capture_begin(const char *title) override;
|
||||
void debug_capture_end() override;
|
||||
void *debug_capture_scope_create(const char *name) override;
|
||||
bool debug_capture_scope_begin(void *scope) override;
|
||||
void debug_capture_scope_end(void *scope) override;
|
||||
|
||||
void debug_unbind_all_ubo() override;
|
||||
void debug_unbind_all_ssbo() override;
|
||||
|
||||
private:
|
||||
static void orphans_add(Vector<GLuint> &orphan_list, std::mutex &list_mutex, GLuint id);
|
||||
void orphans_clear();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLContext")
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
558
blender-5.2.0/source/blender/gpu/opengl/gl_debug.cc
Normal file
558
blender-5.2.0/source/blender/gpu/opengl/gl_debug.cc
Normal file
@@ -0,0 +1,558 @@
|
||||
/* SPDX-FileCopyrightText: 2005 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Debug features of OpenGL.
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_system.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "GPU_debug.hh"
|
||||
#include "GPU_platform.hh"
|
||||
#include "gpu_profile_report.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "gl_backend.hh"
|
||||
#include "gl_context.hh"
|
||||
#include "gl_uniform_buffer.hh"
|
||||
|
||||
#include "gl_debug.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.debug"};
|
||||
|
||||
/* Avoid too much NVidia buffer info in the output log. */
|
||||
#define TRIM_NVIDIA_BUFFER_INFO 1
|
||||
/* Avoid unneeded shader statistics. */
|
||||
#define TRIM_SHADER_STATS_INFO 1
|
||||
|
||||
namespace gpu::debug {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug Callbacks
|
||||
*
|
||||
* Hooks up debug callbacks to a debug OpenGL context using extensions or 4.3 core debug
|
||||
* capabilities.
|
||||
* \{ */
|
||||
|
||||
/* Debug callbacks need the same calling convention as OpenGL functions. */
|
||||
#if defined(_WIN32)
|
||||
# define APIENTRY __stdcall
|
||||
#else
|
||||
# define APIENTRY
|
||||
#endif
|
||||
|
||||
static void APIENTRY debug_callback(GLenum /*source*/,
|
||||
GLenum type,
|
||||
GLuint /*id*/,
|
||||
GLenum severity,
|
||||
GLsizei /*length*/,
|
||||
const GLchar *message,
|
||||
const GLvoid * /*userParm*/)
|
||||
{
|
||||
if (ELEM(type, GL_DEBUG_TYPE_PUSH_GROUP, GL_DEBUG_TYPE_POP_GROUP)) {
|
||||
/* The debug layer will emit a message each time a debug group is pushed or popped.
|
||||
* We use that for easy command grouping inside frame analyzer tools. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* NOTE: callback function can be triggered during before the platform is initialized.
|
||||
* In this case invoking `GPU_type_matches` would fail and
|
||||
* therefore the message is checked before the platform matching. */
|
||||
if (TRIM_NVIDIA_BUFFER_INFO && STRPREFIX(message, "Buffer detailed info") &&
|
||||
GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL))
|
||||
{
|
||||
/* Suppress buffer information flooding the output. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (TRIM_SHADER_STATS_INFO && STRPREFIX(message, "Shader Stats")) {
|
||||
/* Suppress buffer information flooding the output. */
|
||||
return;
|
||||
}
|
||||
|
||||
const bool use_color = CLG_color_support_get(&LOG);
|
||||
|
||||
if (ELEM(severity, GL_DEBUG_SEVERITY_LOW, GL_DEBUG_SEVERITY_NOTIFICATION)) {
|
||||
if (CLOG_CHECK(&LOG, CLG_LEVEL_INFO)) {
|
||||
const char *format = use_color ? "\033[2m%s\033[0m" : "%s";
|
||||
CLG_logf(LOG.type, CLG_LEVEL_INFO, "Notification", "", format, message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
char debug_groups[512] = "";
|
||||
GPU_debug_get_groups_names(sizeof(debug_groups), debug_groups);
|
||||
CLG_Level clog_level;
|
||||
|
||||
if (GPU_debug_group_match(GPU_DEBUG_SHADER_COMPILATION_GROUP) ||
|
||||
GPU_debug_group_match(GPU_DEBUG_SHADER_SPECIALIZATION_GROUP))
|
||||
{
|
||||
/* Do not duplicate shader compilation error/warnings. */
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case GL_DEBUG_TYPE_ERROR:
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||
clog_level = CLG_LEVEL_ERROR;
|
||||
break;
|
||||
case GL_DEBUG_TYPE_PORTABILITY:
|
||||
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
case GL_DEBUG_TYPE_MARKER: /* KHR has this, ARB does not */
|
||||
default:
|
||||
clog_level = CLG_LEVEL_WARN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (CLOG_CHECK(&LOG, clog_level)) {
|
||||
CLG_logf(LOG.type, clog_level, debug_groups, "", "%s", message);
|
||||
if (severity == GL_DEBUG_SEVERITY_HIGH) {
|
||||
/* Focus on error message. */
|
||||
if (use_color) {
|
||||
fprintf(stderr, "\033[2m");
|
||||
}
|
||||
BLI_system_backtrace(stderr);
|
||||
if (use_color) {
|
||||
fprintf(stderr, "\033[0m\n");
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef APIENTRY
|
||||
|
||||
void init_gl_callbacks()
|
||||
{
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(static_cast<GLDEBUGPROC>(debug_callback), nullptr);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
|
||||
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION,
|
||||
GL_DEBUG_TYPE_MARKER,
|
||||
0,
|
||||
GL_DEBUG_SEVERITY_NOTIFICATION,
|
||||
-1,
|
||||
"Successfully hooked OpenGL debug callback");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Error Checking
|
||||
*
|
||||
* This is only useful for implementation that does not support the KHR_debug extension OR when the
|
||||
* implementations do not report any errors even when clearly doing shady things.
|
||||
* \{ */
|
||||
|
||||
void check_gl_error(const char *info)
|
||||
{
|
||||
if (!(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
GLenum error = glGetError();
|
||||
|
||||
#define ERROR_CASE(err) \
|
||||
case err: { \
|
||||
char msg[256]; \
|
||||
SNPRINTF(msg, "%s : %s", #err, info); \
|
||||
debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr); \
|
||||
break; \
|
||||
}
|
||||
|
||||
switch (error) {
|
||||
ERROR_CASE(GL_INVALID_ENUM)
|
||||
ERROR_CASE(GL_INVALID_VALUE)
|
||||
ERROR_CASE(GL_INVALID_OPERATION)
|
||||
ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION)
|
||||
ERROR_CASE(GL_OUT_OF_MEMORY)
|
||||
ERROR_CASE(GL_STACK_UNDERFLOW)
|
||||
ERROR_CASE(GL_STACK_OVERFLOW)
|
||||
case GL_NO_ERROR:
|
||||
break;
|
||||
default:
|
||||
char msg[256];
|
||||
SNPRINTF(msg, "Unknown GL error: %x : %s", error, info);
|
||||
debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void check_gl_resources(const char *info)
|
||||
{
|
||||
if (!(G.debug & G_DEBUG_GPU)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLContext *ctx = GLContext::get();
|
||||
ShaderInterface *interface = ctx->shader->interface;
|
||||
/* NOTE: This only check binding. To be valid, the bound ubo needs to
|
||||
* be big enough to feed the data range the shader awaits. */
|
||||
uint16_t ubo_needed = interface->enabled_ubo_mask_;
|
||||
ubo_needed &= ~ctx->bound_ubo_slots;
|
||||
/* NOTE: This only check binding. To be valid, the bound ssbo needs to
|
||||
* be big enough to feed the data range the shader awaits. */
|
||||
uint16_t ssbo_needed = interface->enabled_ssbo_mask_;
|
||||
ssbo_needed &= ~ctx->bound_ssbo_slots;
|
||||
/* NOTE: This only check binding. To be valid, the bound texture needs to
|
||||
* be the same format/target the shader expects. */
|
||||
uint64_t tex_needed = interface->enabled_tex_mask_;
|
||||
tex_needed &= ~GLContext::state_manager_active_get()->bound_texture_slots();
|
||||
/* NOTE: This only check binding. To be valid, the bound image needs to
|
||||
* be the same format/target the shader expects. */
|
||||
uint8_t ima_needed = interface->enabled_ima_mask_;
|
||||
ima_needed &= ~GLContext::state_manager_active_get()->bound_image_slots();
|
||||
|
||||
if (ubo_needed == 0 && tex_needed == 0 && ima_needed == 0 && ssbo_needed == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; ubo_needed != 0; i++, ubo_needed >>= 1) {
|
||||
if ((ubo_needed & 1) != 0) {
|
||||
const ShaderInput *ubo_input = interface->ubo_get(i);
|
||||
const char *ubo_name = interface->input_name_get(ubo_input);
|
||||
const StringRefNull sh_name = ctx->shader->name_get();
|
||||
char msg[256];
|
||||
SNPRINTF(
|
||||
msg, "Missing UBO bind at slot %d : %s > %s : %s", i, sh_name.c_str(), ubo_name, info);
|
||||
debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; ssbo_needed != 0; i++, ssbo_needed >>= 1) {
|
||||
if ((ssbo_needed & 1) != 0) {
|
||||
const ShaderInput *ssbo_input = interface->ssbo_get(i);
|
||||
const char *ssbo_name = interface->input_name_get(ssbo_input);
|
||||
const StringRefNull sh_name = ctx->shader->name_get();
|
||||
char msg[256];
|
||||
SNPRINTF(
|
||||
msg, "Missing SSBO bind at slot %d : %s > %s : %s", i, sh_name.c_str(), ssbo_name, info);
|
||||
debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; tex_needed != 0; i++, tex_needed >>= 1) {
|
||||
if ((tex_needed & 1) != 0) {
|
||||
/* FIXME: texture_get might return an image input instead. */
|
||||
const ShaderInput *tex_input = interface->texture_get(i);
|
||||
const char *tex_name = interface->input_name_get(tex_input);
|
||||
const StringRefNull sh_name = ctx->shader->name_get();
|
||||
char msg[256];
|
||||
SNPRINTF(msg,
|
||||
"Missing Texture bind at slot %d : %s > %s : %s",
|
||||
i,
|
||||
sh_name.c_str(),
|
||||
tex_name,
|
||||
info);
|
||||
debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; ima_needed != 0; i++, ima_needed >>= 1) {
|
||||
if ((ima_needed & 1) != 0) {
|
||||
/* FIXME: texture_get might return a texture input instead. */
|
||||
const ShaderInput *tex_input = interface->texture_get(i);
|
||||
const char *tex_name = interface->input_name_get(tex_input);
|
||||
const StringRefNull sh_name = ctx->shader->name_get();
|
||||
char msg[256];
|
||||
SNPRINTF(
|
||||
msg, "Missing Image bind at slot %d : %s > %s : %s", i, sh_name.c_str(), tex_name, info);
|
||||
debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void raise_gl_error(const char *info)
|
||||
{
|
||||
debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, info, nullptr);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Label
|
||||
*
|
||||
* Useful for debugging through render-doc. Only defined if using `--debug-gpu`.
|
||||
* Make sure to bind the object first so that it gets defined by the GL implementation.
|
||||
* \{ */
|
||||
|
||||
static const char *to_str_prefix(GLenum type)
|
||||
{
|
||||
switch (type) {
|
||||
case GL_FRAGMENT_SHADER:
|
||||
case GL_GEOMETRY_SHADER:
|
||||
case GL_VERTEX_SHADER:
|
||||
case GL_SHADER:
|
||||
case GL_PROGRAM:
|
||||
return "SHD-";
|
||||
case GL_SAMPLER:
|
||||
return "SAM-";
|
||||
case GL_TEXTURE:
|
||||
return "TEX-";
|
||||
case GL_FRAMEBUFFER:
|
||||
return "FBO-";
|
||||
case GL_VERTEX_ARRAY:
|
||||
return "VAO-";
|
||||
case GL_UNIFORM_BUFFER:
|
||||
return "UBO-";
|
||||
case GL_BUFFER:
|
||||
return "BUF-";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
static const char *to_str_suffix(GLenum type)
|
||||
{
|
||||
switch (type) {
|
||||
case GL_FRAGMENT_SHADER:
|
||||
return "-Frag";
|
||||
case GL_GEOMETRY_SHADER:
|
||||
return "-Geom";
|
||||
case GL_VERTEX_SHADER:
|
||||
return "-Vert";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void object_label(GLenum type, GLuint object, const char *name)
|
||||
{
|
||||
if ((G.debug & G_DEBUG_GPU) &&
|
||||
(epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
|
||||
{
|
||||
char label[64];
|
||||
SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type));
|
||||
/* Small convenience for caller. */
|
||||
switch (type) {
|
||||
case GL_FRAGMENT_SHADER:
|
||||
case GL_GEOMETRY_SHADER:
|
||||
case GL_VERTEX_SHADER:
|
||||
case GL_COMPUTE_SHADER:
|
||||
type = GL_SHADER;
|
||||
break;
|
||||
case GL_UNIFORM_BUFFER:
|
||||
case GL_SHADER_STORAGE_BUFFER:
|
||||
case GL_ARRAY_BUFFER:
|
||||
case GL_ELEMENT_ARRAY_BUFFER:
|
||||
type = GL_BUFFER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
glObjectLabel(type, object, -1, label);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu::debug
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debug Groups
|
||||
*
|
||||
* Useful for debugging through render-doc. This makes all the API calls grouped into "passes".
|
||||
* \{ */
|
||||
|
||||
void GLContext::debug_group_begin(const char *name, int index)
|
||||
{
|
||||
if ((G.debug & G_DEBUG_GPU) &&
|
||||
(epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
|
||||
{
|
||||
/* Add 10 to avoid collision with other indices from other possible callback layers. */
|
||||
index += 10;
|
||||
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, index, -1, name);
|
||||
}
|
||||
|
||||
if (!G.profile_gpu) {
|
||||
return;
|
||||
}
|
||||
|
||||
TimeQuery query = {};
|
||||
query.name = name;
|
||||
query.finished = false;
|
||||
|
||||
glGetInteger64v(GL_TIMESTAMP, &query.cpu_start);
|
||||
/* Use GL_TIMESTAMP instead of GL_ELAPSED_TIME to support nested debug groups */
|
||||
glGenQueries(2, query.handles);
|
||||
glQueryCounter(query.handle_start, GL_TIMESTAMP);
|
||||
|
||||
if (frame_timings.is_empty()) {
|
||||
frame_timings.append({});
|
||||
}
|
||||
frame_timings.last().queries.append(query);
|
||||
}
|
||||
|
||||
void GLContext::debug_group_end()
|
||||
{
|
||||
if ((G.debug & G_DEBUG_GPU) &&
|
||||
(epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")))
|
||||
{
|
||||
glPopDebugGroup();
|
||||
}
|
||||
|
||||
if (!G.profile_gpu) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<TimeQuery> &queries = frame_timings.last().queries;
|
||||
for (int i = queries.size() - 1; i >= 0; i--) {
|
||||
TimeQuery &query = queries[i];
|
||||
if (!query.finished) {
|
||||
query.finished = true;
|
||||
glQueryCounter(query.handle_end, GL_TIMESTAMP);
|
||||
glGetInteger64v(GL_TIMESTAMP, &query.cpu_end);
|
||||
break;
|
||||
}
|
||||
if (i == 0) {
|
||||
CLOG_ERROR(&LOG, "Profile GPU error: Extra GPU_debug_group_end() call.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLContext::process_frame_timings()
|
||||
{
|
||||
if (!G.profile_gpu) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int frame_i = 0; frame_i < frame_timings.size(); frame_i++) {
|
||||
Vector<TimeQuery> &queries = frame_timings[frame_i].queries;
|
||||
|
||||
GLint frame_is_ready = 0;
|
||||
bool frame_is_valid = !queries.is_empty();
|
||||
|
||||
for (int i = queries.size() - 1; i >= 0; i--) {
|
||||
if (!queries[i].finished) {
|
||||
frame_is_valid = false;
|
||||
CLOG_ERROR(&LOG, "Profile GPU error: Missing GPU_debug_group_end() call");
|
||||
}
|
||||
else {
|
||||
glGetQueryObjectiv(queries.last().handle_end, GL_QUERY_RESULT_AVAILABLE, &frame_is_ready);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!frame_is_valid) {
|
||||
/* Cleanup. */
|
||||
for (TimeQuery &query : queries) {
|
||||
glDeleteQueries(2, query.handles);
|
||||
}
|
||||
frame_timings.remove(frame_i--);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!frame_is_ready) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (TimeQuery &query : queries) {
|
||||
GLuint64 gpu_start = 0;
|
||||
GLuint64 gpu_end = 0;
|
||||
glGetQueryObjectui64v(query.handle_start, GL_QUERY_RESULT, &gpu_start);
|
||||
glGetQueryObjectui64v(query.handle_end, GL_QUERY_RESULT, &gpu_end);
|
||||
glDeleteQueries(2, query.handles);
|
||||
|
||||
ProfileReport::get().add_group(
|
||||
query.name, gpu_start, gpu_end, query.cpu_start, query.cpu_end);
|
||||
}
|
||||
|
||||
frame_timings.remove(frame_i--);
|
||||
}
|
||||
|
||||
frame_timings.append({});
|
||||
}
|
||||
|
||||
bool GLContext::debug_capture_begin(const char *title)
|
||||
{
|
||||
return GLBackend::get()->debug_capture_begin(title);
|
||||
}
|
||||
|
||||
bool GLBackend::debug_capture_begin(const char *title)
|
||||
{
|
||||
#ifdef WITH_RENDERDOC
|
||||
if (G.debug & G_DEBUG_GPU_RENDERDOC) {
|
||||
bool result = renderdoc_.start_frame_capture(nullptr, nullptr);
|
||||
if (result && title) {
|
||||
renderdoc_.set_frame_capture_title(title);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
UNUSED_VARS(title);
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLContext::debug_capture_end()
|
||||
{
|
||||
GLBackend::get()->debug_capture_end();
|
||||
}
|
||||
|
||||
void GLBackend::debug_capture_end()
|
||||
{
|
||||
#ifdef WITH_RENDERDOC
|
||||
if (G.debug & G_DEBUG_GPU_RENDERDOC) {
|
||||
renderdoc_.end_frame_capture(nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void *GLContext::debug_capture_scope_create(const char *name)
|
||||
{
|
||||
return (void *)name;
|
||||
}
|
||||
|
||||
bool GLContext::debug_capture_scope_begin(void *scope)
|
||||
{
|
||||
#ifdef WITH_RENDERDOC
|
||||
const char *title = (const char *)scope;
|
||||
if (StringRefNull(title) != StringRefNull(G.gpu_debug_scope_name)) {
|
||||
return false;
|
||||
}
|
||||
GLBackend::get()->debug_capture_begin(title);
|
||||
#else
|
||||
UNUSED_VARS(scope);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLContext::debug_capture_scope_end(void *scope)
|
||||
{
|
||||
#ifdef WITH_RENDERDOC
|
||||
const char *title = (const char *)scope;
|
||||
if (StringRefNull(title) == StringRefNull(G.gpu_debug_scope_name)) {
|
||||
GLBackend::get()->debug_capture_end();
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(scope);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLContext::debug_unbind_all_ubo()
|
||||
{
|
||||
this->bound_ubo_slots = 0u;
|
||||
}
|
||||
|
||||
void GLContext::debug_unbind_all_ssbo()
|
||||
{
|
||||
this->bound_ssbo_slots = 0u;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
31
blender-5.2.0/source/blender/gpu/opengl/gl_debug.hh
Normal file
31
blender-5.2.0/source/blender/gpu/opengl/gl_debug.hh
Normal file
@@ -0,0 +1,31 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gl_context.hh"
|
||||
|
||||
#ifndef NDEBUG
|
||||
# define GL_CHECK_RESOURCES(info) debug::check_gl_resources(info)
|
||||
#else
|
||||
# define GL_CHECK_RESOURCES(info)
|
||||
#endif
|
||||
|
||||
namespace blender::gpu::debug {
|
||||
|
||||
void raise_gl_error(const char *info);
|
||||
void check_gl_error(const char *info);
|
||||
void check_gl_resources(const char *info);
|
||||
/**
|
||||
* This function needs to be called once per context.
|
||||
*/
|
||||
void init_gl_callbacks();
|
||||
|
||||
void object_label(GLenum type, GLuint object, const char *name);
|
||||
|
||||
} // namespace blender::gpu::debug
|
||||
624
blender-5.2.0/source/blender/gpu/opengl/gl_framebuffer.cc
Normal file
624
blender-5.2.0/source/blender/gpu/opengl/gl_framebuffer.cc
Normal file
@@ -0,0 +1,624 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "gl_backend.hh"
|
||||
#include "gl_debug.hh"
|
||||
#include "gl_state.hh"
|
||||
#include "gl_texture.hh"
|
||||
|
||||
#include "gl_framebuffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
GLFrameBuffer::GLFrameBuffer(const char *name) : FrameBuffer(name)
|
||||
{
|
||||
/* Just-In-Time init. See #GLFrameBuffer::init(). */
|
||||
immutable_ = false;
|
||||
fbo_id_ = 0;
|
||||
}
|
||||
|
||||
GLFrameBuffer::GLFrameBuffer(
|
||||
const char *name, GLContext *ctx, GLenum target, GLuint fbo, int w, int h)
|
||||
: FrameBuffer(name)
|
||||
{
|
||||
context_ = ctx;
|
||||
context_id_ = context_->context_id;
|
||||
state_manager_ = static_cast<GLStateManager *>(ctx->state_manager);
|
||||
immutable_ = true;
|
||||
fbo_id_ = fbo;
|
||||
gl_attachments_[0] = target;
|
||||
set_color_attachment_bit(GPU_FB_COLOR_ATTACHMENT0, true);
|
||||
/* Never update an internal frame-buffer. */
|
||||
dirty_attachments_ = false;
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
srgb_ = false;
|
||||
|
||||
viewport_[0][0] = scissor_[0] = 0;
|
||||
viewport_[0][1] = scissor_[1] = 0;
|
||||
viewport_[0][2] = scissor_[2] = w;
|
||||
viewport_[0][3] = scissor_[3] = h;
|
||||
|
||||
if (fbo_id_) {
|
||||
debug::object_label(GL_FRAMEBUFFER, fbo_id_, name_);
|
||||
}
|
||||
}
|
||||
|
||||
GLFrameBuffer::~GLFrameBuffer()
|
||||
{
|
||||
if (context_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GLBackend::get()->is_valid_context_id(context_id_)) {
|
||||
/* Context was freed. It can happen for GLTexture::framebuffer_. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Context might be partially freed. This happens when destroying the window frame-buffers. */
|
||||
if (context_ == Context::get()) {
|
||||
glDeleteFramebuffers(1, &fbo_id_);
|
||||
}
|
||||
else {
|
||||
context_->fbo_free(fbo_id_);
|
||||
}
|
||||
/* Restore default frame-buffer if this frame-buffer was bound. */
|
||||
if (context_->active_fb == this && context_->back_left != this) {
|
||||
/* If this assert triggers it means the frame-buffer is being freed while in use by another
|
||||
* context which, by the way, is TOTALLY UNSAFE! */
|
||||
BLI_assert(context_ == Context::get());
|
||||
GPU_framebuffer_restore();
|
||||
}
|
||||
}
|
||||
|
||||
void GLFrameBuffer::init()
|
||||
{
|
||||
context_ = GLContext::get();
|
||||
context_id_ = context_->context_id;
|
||||
state_manager_ = static_cast<GLStateManager *>(context_->state_manager);
|
||||
glGenFramebuffers(1, &fbo_id_);
|
||||
/* Binding before setting the label is needed on some drivers.
|
||||
* This is not an issue since we call this function only before binding. */
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
|
||||
|
||||
debug::object_label(GL_FRAMEBUFFER, fbo_id_, name_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Config
|
||||
* \{ */
|
||||
|
||||
bool GLFrameBuffer::check(char err_out[256])
|
||||
{
|
||||
this->bind(true);
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
|
||||
#define FORMAT_STATUS(X) \
|
||||
case X: { \
|
||||
err = #X; \
|
||||
break; \
|
||||
}
|
||||
|
||||
const char *err;
|
||||
switch (status) {
|
||||
FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
|
||||
FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
|
||||
FORMAT_STATUS(GL_FRAMEBUFFER_UNSUPPORTED);
|
||||
FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER);
|
||||
FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER);
|
||||
FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE);
|
||||
FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS);
|
||||
FORMAT_STATUS(GL_FRAMEBUFFER_UNDEFINED);
|
||||
case GL_FRAMEBUFFER_COMPLETE:
|
||||
return true;
|
||||
default:
|
||||
err = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
#undef FORMAT_STATUS
|
||||
|
||||
const char *format = "gpu::FrameBuffer: %s status %s\n";
|
||||
|
||||
if (err_out) {
|
||||
BLI_snprintf(err_out, 256, format, this->name_, err);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, format, this->name_, err);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLFrameBuffer::update_attachments()
|
||||
{
|
||||
/* Default frame-buffers cannot have attachments. */
|
||||
BLI_assert(immutable_ == false);
|
||||
|
||||
/* First color texture OR the depth texture if no color is attached.
|
||||
* Used to determine frame-buffer color-space and dimensions. */
|
||||
GPUAttachmentType first_attachment = GPU_FB_MAX_ATTACHMENT;
|
||||
/* NOTE: Inverse iteration to get the first color texture. */
|
||||
for (GPUAttachmentType type = GPU_FB_MAX_ATTACHMENT - 1; type >= 0; --type) {
|
||||
GPUAttachment &attach = attachments_[type];
|
||||
GLenum gl_attachment = to_gl(type);
|
||||
|
||||
if (type >= GPU_FB_COLOR_ATTACHMENT0) {
|
||||
gl_attachments_[type - GPU_FB_COLOR_ATTACHMENT0] = (attach.tex) ? gl_attachment : GL_NONE;
|
||||
first_attachment = (attach.tex) ? type : first_attachment;
|
||||
}
|
||||
else if (first_attachment == GPU_FB_MAX_ATTACHMENT) {
|
||||
/* Only use depth texture to get information if there is no color attachment. */
|
||||
first_attachment = (attach.tex) ? type : first_attachment;
|
||||
}
|
||||
|
||||
if (attach.tex == nullptr) {
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0);
|
||||
continue;
|
||||
}
|
||||
GLuint gl_tex = static_cast<GLTexture *>(attach.tex)->tex_id_;
|
||||
if (attach.layer > -1 && GPU_texture_is_cube(attach.tex) && !GPU_texture_is_array(attach.tex))
|
||||
{
|
||||
/* Could be avoided if ARB_direct_state_access is required. In this case
|
||||
* #glFramebufferTextureLayer would bind the correct face. */
|
||||
GLenum gl_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach.layer;
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, gl_target, gl_tex, attach.mip);
|
||||
}
|
||||
else if (attach.layer > -1) {
|
||||
glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attachment, gl_tex, attach.mip, attach.layer);
|
||||
}
|
||||
else {
|
||||
/* The whole texture level is attached. The frame-buffer is potentially layered. */
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, gl_tex, attach.mip);
|
||||
}
|
||||
/* We found one depth buffer type. Stop here, otherwise we would
|
||||
* override it by setting GPU_FB_DEPTH_ATTACHMENT */
|
||||
if (type == GPU_FB_DEPTH_STENCIL_ATTACHMENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GLContext::unused_fb_slot_workaround) {
|
||||
/* Fill normally un-occupied slots to avoid rendering artifacts on some hardware. */
|
||||
GLuint gl_tex = 0;
|
||||
/* NOTE: Inverse iteration to get the first color texture. */
|
||||
for (int i = ARRAY_SIZE(gl_attachments_) - 1; i >= 0; --i) {
|
||||
GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i;
|
||||
GPUAttachment &attach = attachments_[type];
|
||||
if (attach.tex != nullptr) {
|
||||
gl_tex = static_cast<GLTexture *>(attach.tex)->tex_id_;
|
||||
}
|
||||
else if (gl_tex != 0) {
|
||||
GLenum gl_attachment = to_gl(type);
|
||||
gl_attachments_[i] = gl_attachment;
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, gl_tex, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first_attachment != GPU_FB_MAX_ATTACHMENT) {
|
||||
GPUAttachment &attach = attachments_[first_attachment];
|
||||
int size[3];
|
||||
GPU_texture_get_mipmap_size(attach.tex, attach.mip, size);
|
||||
this->size_set(size[0], size[1]);
|
||||
srgb_ = (GPU_texture_format(attach.tex) == TextureFormat::SRGBA_8_8_8_8);
|
||||
}
|
||||
else {
|
||||
/* Empty frame-buffer. */
|
||||
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, width_);
|
||||
glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, height_);
|
||||
}
|
||||
|
||||
dirty_attachments_ = false;
|
||||
|
||||
glDrawBuffers(ARRAY_SIZE(gl_attachments_), gl_attachments_);
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
BLI_assert(this->check(nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
void GLFrameBuffer::subpass_transition_impl(const GPUAttachmentState depth_attachment_state,
|
||||
Span<GPUAttachmentState> color_attachment_states)
|
||||
{
|
||||
GPU_depth_mask(depth_attachment_state == GPU_ATTACHMENT_WRITE);
|
||||
|
||||
bool any_read = false;
|
||||
for (auto attachment : color_attachment_states.index_range()) {
|
||||
if (attachment == GPU_ATTACHMENT_READ) {
|
||||
any_read = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GLContext::framebuffer_fetch_support) {
|
||||
if (any_read) {
|
||||
glFramebufferFetchBarrierEXT();
|
||||
}
|
||||
}
|
||||
else if (GLContext::texture_barrier_support) {
|
||||
if (any_read) {
|
||||
glTextureBarrier();
|
||||
}
|
||||
|
||||
GLenum attachments[GPU_FB_MAX_COLOR_ATTACHMENT] = {GL_NONE};
|
||||
for (int i : color_attachment_states.index_range()) {
|
||||
GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i;
|
||||
gpu::Texture *attach_tex = this->attachments_[type].tex;
|
||||
if (color_attachment_states[i] == GPU_ATTACHMENT_READ) {
|
||||
tmp_detached_[type] = this->attachments_[type]; /* Bypass feedback loop check. */
|
||||
GPU_texture_bind_ex(attach_tex, GPUSamplerState::default_sampler(), i);
|
||||
}
|
||||
else {
|
||||
tmp_detached_[type] = GPU_ATTACHMENT_NONE;
|
||||
}
|
||||
bool attach_write = color_attachment_states[i] == GPU_ATTACHMENT_WRITE;
|
||||
attachments[i] = (attach_tex && attach_write) ? to_gl(type) : GL_NONE;
|
||||
}
|
||||
/* We have to use `glDrawBuffers` instead of `glColorMaski` because the later is overwritten
|
||||
* by the `GLStateManager`. */
|
||||
/* WATCH(fclem): This modifies the frame-buffer state without setting `dirty_attachments_`. */
|
||||
glDrawBuffers(ARRAY_SIZE(attachments), attachments);
|
||||
}
|
||||
else {
|
||||
/* The only way to have correct visibility without extensions and ensure defined behavior, is
|
||||
* to unbind the textures and update the frame-buffer. This is a slow operation but that's all
|
||||
* we can do to emulate the sub-pass input. */
|
||||
/* TODO(@fclem): Could avoid the frame-buffer reconfiguration by creating multiple
|
||||
* frame-buffers internally. */
|
||||
for (int i : color_attachment_states.index_range()) {
|
||||
GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i;
|
||||
|
||||
if (color_attachment_states[i] == GPU_ATTACHMENT_WRITE) {
|
||||
if (tmp_detached_[type].tex != nullptr) {
|
||||
/* Re-attach previous read attachments. */
|
||||
this->attachment_set(type, tmp_detached_[type]);
|
||||
tmp_detached_[type] = GPU_ATTACHMENT_NONE;
|
||||
}
|
||||
}
|
||||
else if (color_attachment_states[i] == GPU_ATTACHMENT_READ) {
|
||||
tmp_detached_[type] = this->attachments_[type];
|
||||
tmp_detached_[type].tex->detach_from(this);
|
||||
GPU_texture_bind_ex(tmp_detached_[type].tex, GPUSamplerState::default_sampler(), i);
|
||||
}
|
||||
}
|
||||
if (dirty_attachments_) {
|
||||
this->update_attachments();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFrameBuffer::attachment_set_loadstore_op(GPUAttachmentType type, GPULoadStore ls)
|
||||
{
|
||||
BLI_assert(context_->active_fb == this);
|
||||
|
||||
/* TODO(fclem): Add support for other ops. */
|
||||
if (ls.load_action == GPULoadOp::GPU_LOADACTION_CLEAR) {
|
||||
if (tmp_detached_[type].tex != nullptr) {
|
||||
/* #GPULoadStore is used to define the frame-buffer before it is used for rendering.
|
||||
* Binding back unattached attachment makes its state undefined. This is described by the
|
||||
* documentation and the user-land code should specify a sub-pass at the start of the drawing
|
||||
* to explicitly set attachment state. */
|
||||
if (GLContext::framebuffer_fetch_support) {
|
||||
/* NOOP. */
|
||||
}
|
||||
else if (GLContext::texture_barrier_support) {
|
||||
/* Reset default attachment state. */
|
||||
for (int i : IndexRange(ARRAY_SIZE(tmp_detached_))) {
|
||||
tmp_detached_[i] = GPU_ATTACHMENT_NONE;
|
||||
}
|
||||
glDrawBuffers(ARRAY_SIZE(gl_attachments_), gl_attachments_);
|
||||
}
|
||||
else {
|
||||
tmp_detached_[type] = GPU_ATTACHMENT_NONE;
|
||||
this->attachment_set(type, tmp_detached_[type]);
|
||||
this->update_attachments();
|
||||
}
|
||||
}
|
||||
clear_attachment(type, ls.clear_value);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFrameBuffer::apply_state()
|
||||
{
|
||||
if (dirty_state_ == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (multi_viewport_ == false) {
|
||||
glViewport(UNPACK4(viewport_[0]));
|
||||
}
|
||||
else {
|
||||
/* Great API you have there! You have to convert to float values for setting int viewport
|
||||
* values. **Audible Facepalm** */
|
||||
float viewports_f[GPU_MAX_VIEWPORTS][4];
|
||||
for (int i = 0; i < GPU_MAX_VIEWPORTS; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
viewports_f[i][j] = viewport_[i][j];
|
||||
}
|
||||
}
|
||||
glViewportArrayv(0, GPU_MAX_VIEWPORTS, viewports_f[0]);
|
||||
}
|
||||
glScissor(UNPACK4(scissor_));
|
||||
|
||||
if (scissor_test_) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
dirty_state_ = false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Binding
|
||||
* \{ */
|
||||
|
||||
void GLFrameBuffer::bind(bool enabled_srgb)
|
||||
{
|
||||
if (!immutable_ && fbo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
if (context_ != GLContext::get()) {
|
||||
BLI_assert_msg(0, "Trying to use the same frame-buffer in multiple context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (context_->active_fb != this) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
|
||||
/* Internal frame-buffers have only one color output and needs to be set every time. */
|
||||
if (immutable_ && fbo_id_ == 0) {
|
||||
glDrawBuffer(gl_attachments_[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!GLContext::texture_barrier_support && !GLContext::framebuffer_fetch_support) {
|
||||
for (int index : IndexRange(GPU_FB_MAX_ATTACHMENT)) {
|
||||
tmp_detached_[index] = GPU_ATTACHMENT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty_attachments_) {
|
||||
this->update_attachments();
|
||||
this->viewport_reset();
|
||||
this->scissor_reset();
|
||||
}
|
||||
|
||||
if (context_->active_fb != this || enabled_srgb_ != enabled_srgb) {
|
||||
enabled_srgb_ = enabled_srgb;
|
||||
if (enabled_srgb && srgb_) {
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
Shader::set_framebuffer_srgb_target(enabled_srgb && srgb_);
|
||||
}
|
||||
|
||||
if (context_->active_fb != this) {
|
||||
context_->active_fb = this;
|
||||
state_manager_->active_fb = this;
|
||||
dirty_state_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operations.
|
||||
* \{ */
|
||||
|
||||
void GLFrameBuffer::clear(GPUFrameBufferBits buffers,
|
||||
const double4 clear_col,
|
||||
float clear_depth,
|
||||
uint clear_stencil)
|
||||
{
|
||||
BLI_assert(GLContext::get() == context_);
|
||||
BLI_assert(context_->active_fb == this);
|
||||
|
||||
/* Save and restore the state. */
|
||||
GPUWriteMask write_mask = GPU_write_mask_get();
|
||||
uint stencil_mask = GPU_stencil_mask_get();
|
||||
GPUStencilTest stencil_test = GPU_stencil_test_get();
|
||||
|
||||
if (buffers & GPU_COLOR_BIT) {
|
||||
if (immutable_) {
|
||||
/* Immutable frame-buffers (default/window) have no texture attachments,
|
||||
* clear via #glClearColor + #glClear (included in the mask below). */
|
||||
GPU_color_mask(true, true, true, true);
|
||||
glClearColor(
|
||||
float(clear_col[0]), float(clear_col[1]), float(clear_col[2]), float(clear_col[3]));
|
||||
}
|
||||
else {
|
||||
int type = GPU_FB_COLOR_ATTACHMENT0;
|
||||
for (int i = 0; type < GPU_FB_MAX_ATTACHMENT; i++, type++) {
|
||||
if (attachments_[type].tex != nullptr) {
|
||||
this->clear_attachment(GPU_FB_COLOR_ATTACHMENT0 + i, clear_col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (buffers & GPU_DEPTH_BIT) {
|
||||
GPU_depth_mask(true);
|
||||
glClearDepth(clear_depth);
|
||||
}
|
||||
if (buffers & GPU_STENCIL_BIT) {
|
||||
GPU_stencil_write_mask_set(0xFFu);
|
||||
GPU_stencil_test(GPU_STENCIL_ALWAYS);
|
||||
glClearStencil(clear_stencil);
|
||||
}
|
||||
|
||||
context_->state_manager->apply_state();
|
||||
|
||||
/* Mutable frame-buffers clear color via per-attachment calls above,
|
||||
* exclude color from the #glClear mask in that case. */
|
||||
GPUFrameBufferBits glclear_buffers = immutable_ ? buffers :
|
||||
GPUFrameBufferBits(buffers & ~GPU_COLOR_BIT);
|
||||
GLbitfield mask = to_gl(glclear_buffers);
|
||||
glClear(mask);
|
||||
|
||||
if (buffers & (GPU_COLOR_BIT | GPU_DEPTH_BIT)) {
|
||||
GPU_write_mask(write_mask);
|
||||
}
|
||||
if (buffers & GPU_STENCIL_BIT) {
|
||||
GPU_stencil_write_mask_set(stencil_mask);
|
||||
GPU_stencil_test(stencil_test);
|
||||
}
|
||||
}
|
||||
|
||||
void GLFrameBuffer::clear_attachment(GPUAttachmentType type, const double4 clear_value)
|
||||
{
|
||||
BLI_assert(GLContext::get() == context_);
|
||||
BLI_assert(context_->active_fb == this);
|
||||
|
||||
/* Save and restore the state. */
|
||||
GPUWriteMask write_mask = GPU_write_mask_get();
|
||||
|
||||
GPU_depth_mask(true);
|
||||
GPU_color_mask(true, true, true, true);
|
||||
|
||||
context_->state_manager->apply_state();
|
||||
|
||||
if (ELEM(type, GPU_FB_DEPTH_ATTACHMENT, GPU_FB_DEPTH_STENCIL_ATTACHMENT)) {
|
||||
glClearDepth(float(clear_value.x));
|
||||
glClear(to_gl(GPU_DEPTH_BIT));
|
||||
}
|
||||
else {
|
||||
int slot = type - GPU_FB_COLOR_ATTACHMENT0;
|
||||
GPUTextureFormatFlag flag = attachments_[type].tex->format_flag_get();
|
||||
if (flag & GPU_FORMAT_FLOAT || flag & GPU_FORMAT_NORMALIZED_INTEGER) {
|
||||
float4 data = float4(clear_value);
|
||||
glClearBufferfv(GL_COLOR, slot, &data.x);
|
||||
}
|
||||
else if (flag & GPU_FORMAT_INTEGER && flag & GPU_FORMAT_SIGNED) {
|
||||
int4 data = int4(clear_value);
|
||||
glClearBufferiv(GL_COLOR, slot, &data.x);
|
||||
}
|
||||
else if (flag & GPU_FORMAT_INTEGER && !(flag & GPU_FORMAT_SIGNED)) {
|
||||
uint4 data = uint4(clear_value);
|
||||
glClearBufferuiv(GL_COLOR, slot, &data.x);
|
||||
}
|
||||
else {
|
||||
BLI_assert_msg(0, "Unhandled data format");
|
||||
}
|
||||
}
|
||||
|
||||
GPU_write_mask(write_mask);
|
||||
}
|
||||
|
||||
void GLFrameBuffer::clear_multi(Span<double4> clear_cols)
|
||||
{
|
||||
int type = GPU_FB_COLOR_ATTACHMENT0;
|
||||
for (int i = 0; type < GPU_FB_MAX_ATTACHMENT; i++, type++) {
|
||||
if (attachments_[type].tex != nullptr) {
|
||||
this->clear_attachment(GPU_FB_COLOR_ATTACHMENT0 + i, clear_cols[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFrameBuffer::read(GPUFrameBufferBits plane,
|
||||
eGPUDataFormat data_format,
|
||||
const int area[4],
|
||||
int channel_len,
|
||||
int slot,
|
||||
void *r_data)
|
||||
{
|
||||
GLenum format, type, mode;
|
||||
mode = gl_attachments_[slot];
|
||||
type = to_gl(data_format);
|
||||
|
||||
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) {
|
||||
format = GL_RED_INTEGER;
|
||||
}
|
||||
break;
|
||||
case GPU_STENCIL_BIT:
|
||||
fprintf(stderr, "GPUFramebuffer: Error: Trying to read stencil bit. Unsupported.");
|
||||
return;
|
||||
default:
|
||||
fprintf(stderr, "GPUFramebuffer: Error: Trying to read more than one frame-buffer plane.");
|
||||
return;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_);
|
||||
glReadBuffer(mode);
|
||||
glReadPixels(UNPACK4(area), format, type, r_data);
|
||||
}
|
||||
|
||||
void GLFrameBuffer::blit_to(
|
||||
GPUFrameBufferBits planes, int src_slot, FrameBuffer *dst_, int dst_slot, int x, int y)
|
||||
{
|
||||
GLFrameBuffer *src = this;
|
||||
GLFrameBuffer *dst = static_cast<GLFrameBuffer *>(dst_);
|
||||
|
||||
/* Frame-buffers must be up to date. This simplify this function. */
|
||||
if (src->dirty_attachments_) {
|
||||
src->bind(true);
|
||||
}
|
||||
if (dst->dirty_attachments_) {
|
||||
dst->bind(true);
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, src->fbo_id_);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->fbo_id_);
|
||||
|
||||
if (planes & GPU_COLOR_BIT) {
|
||||
BLI_assert(src->immutable_ == false || src_slot == 0);
|
||||
BLI_assert(dst->immutable_ == false || dst_slot == 0);
|
||||
BLI_assert(src->gl_attachments_[src_slot] != GL_NONE);
|
||||
BLI_assert(dst->gl_attachments_[dst_slot] != GL_NONE);
|
||||
glReadBuffer(src->gl_attachments_[src_slot]);
|
||||
glDrawBuffer(dst->gl_attachments_[dst_slot]);
|
||||
}
|
||||
|
||||
context_->state_manager->apply_state();
|
||||
|
||||
int w = src->width_;
|
||||
int h = src->height_;
|
||||
GLbitfield mask = to_gl(planes);
|
||||
glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, mask, GL_NEAREST);
|
||||
|
||||
if (!dst->immutable_) {
|
||||
/* Restore the draw buffers. */
|
||||
glDrawBuffers(ARRAY_SIZE(dst->gl_attachments_), dst->gl_attachments_);
|
||||
}
|
||||
/* Ensure previous buffer is restored. */
|
||||
context_->active_fb = dst;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
164
blender-5.2.0/source/blender/gpu/opengl/gl_framebuffer.hh
Normal file
164
blender-5.2.0/source/blender/gpu/opengl/gl_framebuffer.hh
Normal file
@@ -0,0 +1,164 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Encapsulation of Frame-buffer states (attached textures, viewport, scissors).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "gpu_framebuffer_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLStateManager;
|
||||
|
||||
/**
|
||||
* Implementation of FrameBuffer object using OpenGL.
|
||||
*/
|
||||
class GLFrameBuffer : public FrameBuffer {
|
||||
/* For debugging purpose. */
|
||||
friend class GLTexture;
|
||||
|
||||
private:
|
||||
/** OpenGL handle. */
|
||||
GLuint fbo_id_ = 0;
|
||||
/** Context the handle is from. Frame-buffers are not shared across contexts. */
|
||||
GLContext *context_ = nullptr;
|
||||
/** WORKAROUND: GLTexture::framebuffer_ can outlive its context.
|
||||
* We track the context id to ensure we don't try to use context_ after its been freed. */
|
||||
int context_id_ = -1;
|
||||
/** State Manager of the same contexts. */
|
||||
GLStateManager *state_manager_ = nullptr;
|
||||
/** Copy of the GL state. Contains ONLY color attachments enums for slot binding. */
|
||||
GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT] = {0};
|
||||
/** List of attachment that are associated with this frame-buffer but temporarily detached. */
|
||||
GPUAttachment tmp_detached_[GPU_FB_MAX_ATTACHMENT];
|
||||
/** Internal frame-buffers are immutable. */
|
||||
bool immutable_ = false;
|
||||
/** True is the frame-buffer has its first color target using the
|
||||
* TextureFormat::SRGBA_8_8_8_8 format. */
|
||||
bool srgb_ = false;
|
||||
/** True is the frame-buffer has been bound using the GL_FRAMEBUFFER_SRGB feature. */
|
||||
bool enabled_srgb_ = false;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a conventional frame-buffer to attach texture to.
|
||||
*/
|
||||
GLFrameBuffer(const char *name);
|
||||
|
||||
/**
|
||||
* Special frame-buffer encapsulating internal window frame-buffer.
|
||||
* (i.e.: #GL_FRONT_LEFT, #GL_BACK_RIGHT, ...)
|
||||
* \param ctx: Context the handle is from.
|
||||
* \param target: The internal GL name (i.e: #GL_BACK_LEFT).
|
||||
* \param fbo: The (optional) already created object for some implementation. Default is 0.
|
||||
* \param w: Buffer width.
|
||||
* \param h: Buffer height.
|
||||
*/
|
||||
GLFrameBuffer(const char *name, GLContext *ctx, GLenum target, GLuint fbo, int w, int h);
|
||||
|
||||
~GLFrameBuffer();
|
||||
|
||||
void bind(bool enabled_srgb) override;
|
||||
|
||||
/**
|
||||
* This is a rather slow operation. Don't check in normal cases.
|
||||
*/
|
||||
bool check(char err_out[256]) override;
|
||||
|
||||
void clear(GPUFrameBufferBits buffers,
|
||||
const double4 clear_col,
|
||||
float clear_depth,
|
||||
uint clear_stencil) override;
|
||||
void clear_multi(Span<double4> clear_cols) override;
|
||||
void clear_attachment(GPUAttachmentType type, const double4 clear_value) override;
|
||||
|
||||
/* Attachment load-stores are currently no-op's in OpenGL. */
|
||||
void attachment_set_loadstore_op(GPUAttachmentType type, GPULoadStore ls) override;
|
||||
|
||||
protected:
|
||||
void subpass_transition_impl(const GPUAttachmentState depth_attachment_state,
|
||||
Span<GPUAttachmentState> color_attachment_states) override;
|
||||
|
||||
public:
|
||||
void read(GPUFrameBufferBits planes,
|
||||
eGPUDataFormat format,
|
||||
const int area[4],
|
||||
int channel_len,
|
||||
int slot,
|
||||
void *r_data) override;
|
||||
|
||||
/**
|
||||
* Copy \a src at the give offset inside \a dst.
|
||||
*/
|
||||
void blit_to(GPUFrameBufferBits planes,
|
||||
int src_slot,
|
||||
FrameBuffer *dst,
|
||||
int dst_slot,
|
||||
int dst_offset_x,
|
||||
int dst_offset_y) override;
|
||||
|
||||
GLContext *context_get() const
|
||||
{
|
||||
return context_;
|
||||
}
|
||||
|
||||
void apply_state();
|
||||
|
||||
private:
|
||||
void init();
|
||||
void update_attachments();
|
||||
void update_drawbuffers();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLFrameBuffer");
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Enums Conversion
|
||||
* \{ */
|
||||
|
||||
static inline GLenum to_gl(const GPUAttachmentType type)
|
||||
{
|
||||
#define ATTACHMENT(X) \
|
||||
case GPU_FB_##X: { \
|
||||
return GL_##X; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
switch (type) {
|
||||
ATTACHMENT(DEPTH_ATTACHMENT);
|
||||
ATTACHMENT(DEPTH_STENCIL_ATTACHMENT);
|
||||
ATTACHMENT(COLOR_ATTACHMENT0);
|
||||
ATTACHMENT(COLOR_ATTACHMENT1);
|
||||
ATTACHMENT(COLOR_ATTACHMENT2);
|
||||
ATTACHMENT(COLOR_ATTACHMENT3);
|
||||
ATTACHMENT(COLOR_ATTACHMENT4);
|
||||
ATTACHMENT(COLOR_ATTACHMENT5);
|
||||
ATTACHMENT(COLOR_ATTACHMENT6);
|
||||
ATTACHMENT(COLOR_ATTACHMENT7);
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return GL_COLOR_ATTACHMENT0;
|
||||
}
|
||||
#undef ATTACHMENT
|
||||
}
|
||||
|
||||
static inline GLbitfield to_gl(const GPUFrameBufferBits bits)
|
||||
{
|
||||
GLbitfield mask = 0;
|
||||
mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0;
|
||||
mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0;
|
||||
mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0;
|
||||
return mask;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
198
blender-5.2.0/source/blender/gpu/opengl/gl_immediate.cc
Normal file
198
blender-5.2.0/source/blender/gpu/opengl/gl_immediate.cc
Normal file
@@ -0,0 +1,198 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Mimics old style opengl immediate mode drawing.
|
||||
*/
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
#include "gpu_vertex_format_private.hh"
|
||||
|
||||
#include "gl_context.hh"
|
||||
#include "gl_debug.hh"
|
||||
#include "gl_primitive.hh"
|
||||
#include "gl_vertex_array.hh"
|
||||
|
||||
#include "gl_immediate.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
GLImmediate::GLImmediate()
|
||||
{
|
||||
glGenVertexArrays(1, &vao_id_);
|
||||
glBindVertexArray(vao_id_); /* Necessary for glObjectLabel. */
|
||||
|
||||
buffer.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE;
|
||||
glGenBuffers(1, &buffer.vbo_id);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id);
|
||||
glBufferData(GL_ARRAY_BUFFER, buffer.buffer_size, nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
buffer_strict.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE;
|
||||
glGenBuffers(1, &buffer_strict.vbo_id);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer_strict.vbo_id);
|
||||
glBufferData(GL_ARRAY_BUFFER, buffer_strict.buffer_size, nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
debug::object_label(GL_VERTEX_ARRAY, vao_id_, "Immediate");
|
||||
debug::object_label(GL_BUFFER, buffer.vbo_id, "ImmediateVbo");
|
||||
debug::object_label(GL_BUFFER, buffer_strict.vbo_id, "ImmediateVboStrict");
|
||||
}
|
||||
|
||||
GLImmediate::~GLImmediate()
|
||||
{
|
||||
glDeleteVertexArrays(1, &vao_id_);
|
||||
|
||||
glDeleteBuffers(1, &buffer.vbo_id);
|
||||
glDeleteBuffers(1, &buffer_strict.vbo_id);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Buffer management
|
||||
* \{ */
|
||||
|
||||
uchar *GLImmediate::begin()
|
||||
{
|
||||
/* How many bytes do we need for this draw call? */
|
||||
const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len);
|
||||
/* Does the current buffer have enough room? */
|
||||
const size_t available_bytes = buffer_size() - buffer_offset();
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (this->shader->is_polyline) {
|
||||
/* Silence error. These are bound inside `immEnd()`. */
|
||||
GLContext::get()->bound_ssbo_slots |= 1 << GPU_SSBO_POLYLINE_POS_BUF_SLOT;
|
||||
GLContext::get()->bound_ssbo_slots |= 1 << GPU_SSBO_POLYLINE_COL_BUF_SLOT;
|
||||
GLContext::get()->bound_ssbo_slots |= 1 << GPU_SSBO_INDEX_BUF_SLOT;
|
||||
}
|
||||
#endif
|
||||
|
||||
GL_CHECK_RESOURCES("Immediate");
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_id());
|
||||
|
||||
bool recreate_buffer = false;
|
||||
if (bytes_needed > buffer_size()) {
|
||||
/* expand the internal buffer */
|
||||
buffer_size() = bytes_needed;
|
||||
recreate_buffer = true;
|
||||
}
|
||||
else if (bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE &&
|
||||
buffer_size() > DEFAULT_INTERNAL_BUFFER_SIZE)
|
||||
{
|
||||
/* shrink the internal buffer */
|
||||
buffer_size() = DEFAULT_INTERNAL_BUFFER_SIZE;
|
||||
recreate_buffer = true;
|
||||
}
|
||||
|
||||
uint vert_alignment = vertex_format.stride;
|
||||
if (this->shader->is_polyline) {
|
||||
/* Polyline needs to bind the buffer as SSBO.
|
||||
* The start of the range needs to match the SSBO alignment requirements. */
|
||||
vert_alignment = ceil_to_multiple_u(vert_alignment, GPU_storage_buffer_alignment());
|
||||
}
|
||||
/* Ensure vertex data is aligned. Might waste a little space, but it's safe. */
|
||||
const uint pre_padding = padding(buffer_offset(), vert_alignment);
|
||||
|
||||
if (!recreate_buffer && ((bytes_needed + pre_padding) <= available_bytes)) {
|
||||
buffer_offset() += pre_padding;
|
||||
}
|
||||
else {
|
||||
/* orphan this buffer & start with a fresh one */
|
||||
glBufferData(GL_ARRAY_BUFFER, buffer_size(), nullptr, GL_DYNAMIC_DRAW);
|
||||
buffer_offset() = 0;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
GLint bufsize;
|
||||
glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufsize);
|
||||
BLI_assert(buffer_offset() + bytes_needed <= bufsize);
|
||||
}
|
||||
#endif
|
||||
|
||||
GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
|
||||
if (!strict_vertex_len) {
|
||||
access |= GL_MAP_FLUSH_EXPLICIT_BIT;
|
||||
}
|
||||
void *data = glMapBufferRange(GL_ARRAY_BUFFER, buffer_offset(), bytes_needed, access);
|
||||
BLI_assert(data != nullptr);
|
||||
|
||||
bytes_mapped_ = bytes_needed;
|
||||
return static_cast<uchar *>(data);
|
||||
}
|
||||
|
||||
void GLImmediate::end()
|
||||
{
|
||||
BLI_assert(prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
|
||||
|
||||
uint buffer_bytes_used = bytes_mapped_;
|
||||
if (!strict_vertex_len) {
|
||||
if (vertex_idx != vertex_len) {
|
||||
vertex_len = vertex_idx;
|
||||
buffer_bytes_used = vertex_buffer_size(&vertex_format, vertex_len);
|
||||
/* unused buffer bytes are available to the next immBegin */
|
||||
}
|
||||
/* tell OpenGL what range was modified so it doesn't copy the whole mapped range */
|
||||
glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used);
|
||||
}
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
|
||||
if (vertex_len == 0) {
|
||||
/* NOOP. Nothing to draw. */
|
||||
}
|
||||
else if (this->shader->is_polyline) {
|
||||
GLintptr offset = buffer_offset();
|
||||
GLenum target = GL_SHADER_STORAGE_BUFFER;
|
||||
glBindBufferRange(target, GPU_SSBO_POLYLINE_POS_BUF_SLOT, vbo_id(), offset, buffer_bytes_used);
|
||||
glBindBufferRange(target, GPU_SSBO_POLYLINE_COL_BUF_SLOT, vbo_id(), offset, buffer_bytes_used);
|
||||
/* Not used. Satisfy the binding. */
|
||||
glBindBufferRange(target, GPU_SSBO_INDEX_BUF_SLOT, vbo_id(), offset, buffer_bytes_used);
|
||||
|
||||
this->polyline_draw_workaround(0);
|
||||
|
||||
#ifndef NDEBUG
|
||||
GLContext::get()->bound_ssbo_slots &= ~(1 << GPU_SSBO_POLYLINE_POS_BUF_SLOT);
|
||||
GLContext::get()->bound_ssbo_slots &= ~(1 << GPU_SSBO_POLYLINE_COL_BUF_SLOT);
|
||||
GLContext::get()->bound_ssbo_slots &= ~(1 << GPU_SSBO_INDEX_BUF_SLOT);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
GLContext::get()->state_manager->apply_state();
|
||||
|
||||
/* We convert the offset in vertex offset from the buffer's start.
|
||||
* This works because we added some padding to align the first vertex. */
|
||||
uint v_first = buffer_offset() / vertex_format.stride;
|
||||
GLVertArray::update_bindings(
|
||||
vao_id_, v_first, &vertex_format, reinterpret_cast<Shader *>(shader)->interface);
|
||||
|
||||
/* Update matrices. */
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
glDrawArrays(to_gl(prim_type), 0, vertex_len);
|
||||
|
||||
/* These lines are causing crash on startup on some old GPU + drivers.
|
||||
* They are not required so just comment them. (#55722) */
|
||||
// glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
// glBindVertexArray(0);
|
||||
}
|
||||
|
||||
buffer_offset() += buffer_bytes_used;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
64
blender-5.2.0/source/blender/gpu/opengl/gl_immediate.hh
Normal file
64
blender-5.2.0/source/blender/gpu/opengl/gl_immediate.hh
Normal file
@@ -0,0 +1,64 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Mimics old style opengl immediate mode drawing.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "gpu_immediate_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* size of internal buffer */
|
||||
#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024)
|
||||
|
||||
class GLImmediate : public Immediate {
|
||||
private:
|
||||
/* Use two buffers for strict and non-strict vertex count to
|
||||
* avoid some huge driver slowdown (see #70922).
|
||||
* Use accessor functions to get / modify. */
|
||||
struct {
|
||||
/** Opengl Handle for this buffer. */
|
||||
GLuint vbo_id = 0;
|
||||
/** Offset of the mapped data in data. */
|
||||
size_t buffer_offset = 0;
|
||||
/** Size of the whole buffer in bytes. */
|
||||
size_t buffer_size = 0;
|
||||
} buffer, buffer_strict;
|
||||
/** Size in bytes of the mapped region. */
|
||||
size_t bytes_mapped_ = 0;
|
||||
/** Vertex array for this immediate mode instance. */
|
||||
GLuint vao_id_ = 0;
|
||||
|
||||
public:
|
||||
GLImmediate();
|
||||
~GLImmediate();
|
||||
|
||||
uchar *begin() override;
|
||||
void end() override;
|
||||
|
||||
private:
|
||||
GLuint &vbo_id()
|
||||
{
|
||||
return strict_vertex_len ? buffer_strict.vbo_id : buffer.vbo_id;
|
||||
};
|
||||
|
||||
size_t &buffer_offset()
|
||||
{
|
||||
return strict_vertex_len ? buffer_strict.buffer_offset : buffer.buffer_offset;
|
||||
};
|
||||
|
||||
size_t &buffer_size()
|
||||
{
|
||||
return strict_vertex_len ? buffer_strict.buffer_size : buffer.buffer_size;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
105
blender-5.2.0/source/blender/gpu/opengl/gl_index_buffer.cc
Normal file
105
blender-5.2.0/source/blender/gpu/opengl/gl_index_buffer.cc
Normal file
@@ -0,0 +1,105 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "gl_context.hh"
|
||||
|
||||
#include "gl_index_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
GLIndexBuf::~GLIndexBuf()
|
||||
{
|
||||
GLContext::buffer_free(ibo_id_);
|
||||
}
|
||||
|
||||
void GLIndexBuf::bind()
|
||||
{
|
||||
if (is_subrange_) {
|
||||
static_cast<GLIndexBuf *>(src_)->bind();
|
||||
return;
|
||||
}
|
||||
|
||||
const bool allocate_on_device = ibo_id_ == 0;
|
||||
if (allocate_on_device) {
|
||||
glGenBuffers(1, &ibo_id_);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_);
|
||||
|
||||
if (data_ != nullptr || allocate_on_device) {
|
||||
size_t size = this->size_get();
|
||||
/* Pad the buffer to avoid out of bound reads when using vertex pulling mode. */
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ceil_to_multiple_ul(size, 16), nullptr, GL_STATIC_DRAW);
|
||||
|
||||
if (data_ != nullptr) {
|
||||
/* Sends data to GPU. */
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, size, data_);
|
||||
}
|
||||
/* No need to keep copy of data in system memory. */
|
||||
MEM_SAFE_DELETE_VOID(data_);
|
||||
}
|
||||
}
|
||||
|
||||
void GLIndexBuf::bind_as_ssbo(uint binding)
|
||||
{
|
||||
if (is_subrange_) {
|
||||
src_->bind_as_ssbo(binding);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ibo_id_ == 0 || data_ != nullptr) {
|
||||
/* Calling `glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_)` changes the index buffer
|
||||
* of the currently bound VAO.
|
||||
*
|
||||
* In the OpenGL backend, the VAO state persists even after `GLVertArray::update_bindings`
|
||||
* is called.
|
||||
*
|
||||
* NOTE: For safety, we could call `glBindVertexArray(0)` right after drawing a `gpu::Batch`.
|
||||
* However, for performance reasons, we have chosen not to do so. */
|
||||
glBindVertexArray(0);
|
||||
bind();
|
||||
}
|
||||
|
||||
BLI_assert(ibo_id_ != 0);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ibo_id_);
|
||||
|
||||
#ifndef NDEBUG
|
||||
BLI_assert(binding < 16);
|
||||
GLContext::get()->bound_ssbo_slots |= 1 << binding;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLIndexBuf::read(uint32_t *data) const
|
||||
{
|
||||
BLI_assert(is_active());
|
||||
const void *buffer = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
|
||||
memcpy(data, buffer, size_get());
|
||||
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
bool GLIndexBuf::is_active() const
|
||||
{
|
||||
if (!ibo_id_) {
|
||||
return false;
|
||||
}
|
||||
int active_ibo_id = 0;
|
||||
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &active_ibo_id);
|
||||
return ibo_id_ == active_ibo_id;
|
||||
}
|
||||
|
||||
void GLIndexBuf::upload_data()
|
||||
{
|
||||
bind();
|
||||
}
|
||||
|
||||
void GLIndexBuf::update_sub(uint start, uint len, const void *data)
|
||||
{
|
||||
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, start, len, data);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
68
blender-5.2.0/source/blender/gpu/opengl/gl_index_buffer.hh
Normal file
68
blender-5.2.0/source/blender/gpu/opengl/gl_index_buffer.hh
Normal file
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLIndexBuf : public IndexBuf {
|
||||
friend class GLBatch;
|
||||
friend class GLDrawList;
|
||||
friend class GLShader; /* For compute shaders. */
|
||||
|
||||
private:
|
||||
GLuint ibo_id_ = 0;
|
||||
|
||||
public:
|
||||
~GLIndexBuf();
|
||||
|
||||
void bind();
|
||||
void bind_as_ssbo(uint binding) override;
|
||||
|
||||
void read(uint32_t *data) const override;
|
||||
|
||||
void *offset_ptr(uint additional_vertex_offset) const
|
||||
{
|
||||
additional_vertex_offset += index_start_;
|
||||
if (index_type_ == GPU_INDEX_U32) {
|
||||
return reinterpret_cast<void *>(intptr_t(additional_vertex_offset) * sizeof(GLuint));
|
||||
}
|
||||
return reinterpret_cast<void *>(intptr_t(additional_vertex_offset) * sizeof(GLushort));
|
||||
}
|
||||
|
||||
GLuint restart_index() const
|
||||
{
|
||||
return (index_type_ == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
void upload_data() override;
|
||||
|
||||
void update_sub(uint start, uint len, const void *data) override;
|
||||
|
||||
private:
|
||||
bool is_active() const;
|
||||
void strip_restart_indices() override
|
||||
{
|
||||
/* No-op. */
|
||||
}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLIndexBuf")
|
||||
};
|
||||
|
||||
static inline GLenum to_gl(GPUIndexBufType type)
|
||||
{
|
||||
return (type == GPU_INDEX_U32) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
48
blender-5.2.0/source/blender/gpu/opengl/gl_primitive.hh
Normal file
48
blender-5.2.0/source/blender/gpu/opengl/gl_primitive.hh
Normal file
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* Encapsulation of Frame-buffer states (attached textures, viewport, scissors).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
#include "GPU_primitive.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
static inline GLenum to_gl(GPUPrimType prim_type)
|
||||
{
|
||||
BLI_assert(prim_type != GPU_PRIM_NONE);
|
||||
switch (prim_type) {
|
||||
default:
|
||||
case GPU_PRIM_POINTS:
|
||||
return GL_POINTS;
|
||||
case GPU_PRIM_LINES:
|
||||
return GL_LINES;
|
||||
case GPU_PRIM_LINE_STRIP:
|
||||
return GL_LINE_STRIP;
|
||||
case GPU_PRIM_LINE_LOOP:
|
||||
return GL_LINE_LOOP;
|
||||
case GPU_PRIM_TRIS:
|
||||
return GL_TRIANGLES;
|
||||
case GPU_PRIM_TRI_STRIP:
|
||||
return GL_TRIANGLE_STRIP;
|
||||
case GPU_PRIM_TRI_FAN:
|
||||
return GL_TRIANGLE_FAN;
|
||||
|
||||
case GPU_PRIM_LINES_ADJ:
|
||||
return GL_LINES_ADJACENCY;
|
||||
case GPU_PRIM_LINE_STRIP_ADJ:
|
||||
return GL_LINE_STRIP_ADJACENCY;
|
||||
case GPU_PRIM_TRIS_ADJ:
|
||||
return GL_TRIANGLES_ADJACENCY;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
64
blender-5.2.0/source/blender/gpu/opengl/gl_query.cc
Normal file
64
blender-5.2.0/source/blender/gpu/opengl/gl_query.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "gl_query.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
#define QUERY_CHUNCK_LEN 256
|
||||
|
||||
GLQueryPool::~GLQueryPool()
|
||||
{
|
||||
glDeleteQueries(query_ids_.size(), query_ids_.data());
|
||||
}
|
||||
|
||||
void GLQueryPool::init(GPUQueryType type)
|
||||
{
|
||||
BLI_assert(initialized_ == false);
|
||||
initialized_ = true;
|
||||
type_ = type;
|
||||
gl_type_ = to_gl(type);
|
||||
query_issued_ = 0;
|
||||
}
|
||||
|
||||
#if 0 /* TODO: to avoid realloc of permanent query pool. */
|
||||
void GLQueryPool::reset(GPUQueryType type)
|
||||
{
|
||||
initialized_ = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void GLQueryPool::begin_query()
|
||||
{
|
||||
/* TODO: add assert about expected usage. */
|
||||
while (query_issued_ >= query_ids_.size()) {
|
||||
int64_t prev_size = query_ids_.size();
|
||||
int64_t chunk_size = prev_size == 0 ? query_ids_.capacity() : QUERY_CHUNCK_LEN;
|
||||
query_ids_.resize(prev_size + chunk_size);
|
||||
glGenQueries(chunk_size, &query_ids_[prev_size]);
|
||||
}
|
||||
glBeginQuery(gl_type_, query_ids_[query_issued_++]);
|
||||
}
|
||||
|
||||
void GLQueryPool::end_query()
|
||||
{
|
||||
/* TODO: add assert about expected usage. */
|
||||
glEndQuery(gl_type_);
|
||||
}
|
||||
|
||||
void GLQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == query_issued_);
|
||||
|
||||
for (int i = 0; i < query_issued_; i++) {
|
||||
/* NOTE: This is a sync point. */
|
||||
glGetQueryObjectuiv(query_ids_[i], GL_QUERY_RESULT, &r_values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
54
blender-5.2.0/source/blender/gpu/opengl/gl_query.hh
Normal file
54
blender-5.2.0/source/blender/gpu/opengl/gl_query.hh
Normal file
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_query.hh"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLQueryPool : public QueryPool {
|
||||
private:
|
||||
/** Contains queries object handles. */
|
||||
Vector<GLuint, QUERY_MIN_LEN> query_ids_;
|
||||
/** Type of this query pool. */
|
||||
GPUQueryType type_;
|
||||
/** Associated GL type. */
|
||||
GLenum gl_type_;
|
||||
/** Number of queries that have been issued since last initialization.
|
||||
* Should be equal to query_ids_.size(). */
|
||||
uint32_t query_issued_;
|
||||
/** Can only be initialized once. */
|
||||
bool initialized_ = false;
|
||||
|
||||
public:
|
||||
~GLQueryPool();
|
||||
|
||||
void init(GPUQueryType type) override;
|
||||
|
||||
void begin_query() override;
|
||||
void end_query() override;
|
||||
|
||||
void get_occlusion_result(MutableSpan<uint32_t> r_values) override;
|
||||
};
|
||||
|
||||
static inline GLenum to_gl(GPUQueryType type)
|
||||
{
|
||||
if (type == GPU_QUERY_OCCLUSION) {
|
||||
/* TODO(fclem): try with GL_ANY_SAMPLES_PASSED. */
|
||||
return GL_SAMPLES_PASSED;
|
||||
}
|
||||
BLI_assert(0);
|
||||
return GL_SAMPLES_PASSED;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
1991
blender-5.2.0/source/blender/gpu/opengl/gl_shader.cc
Normal file
1991
blender-5.2.0/source/blender/gpu/opengl/gl_shader.cc
Normal file
File diff suppressed because it is too large
Load Diff
291
blender-5.2.0/source/blender/gpu/opengl/gl_shader.hh
Normal file
291
blender-5.2.0/source/blender/gpu/opengl/gl_shader.hh
Normal file
@@ -0,0 +1,291 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_subprocess.hh"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/**
|
||||
* Shaders that uses specialization constants must keep track of the sources in order to rebuild
|
||||
* shader stages.
|
||||
*
|
||||
* Some sources are shared and won't be copied. For example for dependencies. In this case we
|
||||
* would only store the source_ref.
|
||||
*
|
||||
* Other sources would be stored in the #source attribute. #source_ref
|
||||
* would still be updated.
|
||||
*/
|
||||
struct GLSource {
|
||||
std::string source;
|
||||
std::optional<StringRefNull> source_ref;
|
||||
|
||||
GLSource() = default;
|
||||
GLSource(StringRefNull other_source);
|
||||
};
|
||||
class GLSources : public Vector<GLSource> {
|
||||
public:
|
||||
GLSources &operator=(Span<StringRefNull> other);
|
||||
Vector<StringRefNull> sources_get() const;
|
||||
std::string to_string() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* The full sources for each shader stage, baked into a single string from their respective
|
||||
* GLSources. (Can be retrieved from GLShader::get_sources())
|
||||
*/
|
||||
struct GLSourcesBaked : NonCopyable {
|
||||
std::string comp;
|
||||
std::string vert;
|
||||
std::string geom;
|
||||
std::string frag;
|
||||
|
||||
/* Returns the size (in bytes) required to store the source of all the used stages. */
|
||||
size_t size();
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of shader compilation and uniforms handling using OpenGL.
|
||||
*/
|
||||
class GLShader : public Shader {
|
||||
friend shader::ShaderCreateInfo;
|
||||
friend shader::StageInterfaceInfo;
|
||||
friend class GLSubprocessShaderCompiler;
|
||||
friend class GLShaderCompiler;
|
||||
|
||||
private:
|
||||
struct GLProgram {
|
||||
/** Handle for program. */
|
||||
GLuint program_id = 0;
|
||||
/** Handle for individual shader stages. */
|
||||
GLuint vert_shader = 0;
|
||||
GLuint geom_shader = 0;
|
||||
GLuint frag_shader = 0;
|
||||
GLuint compute_shader = 0;
|
||||
|
||||
std::mutex compilation_mutex;
|
||||
|
||||
GLProgram() {}
|
||||
~GLProgram();
|
||||
|
||||
void program_link(StringRefNull shader_name);
|
||||
};
|
||||
|
||||
using GLProgramCacheKey = Vector<shader::SpecializationConstant::Value>;
|
||||
/** Contains all specialized shader variants. */
|
||||
Map<GLProgramCacheKey, std::unique_ptr<GLProgram>> program_cache_;
|
||||
|
||||
std::mutex program_cache_mutex_;
|
||||
|
||||
/** Main program instance. This is the default specialized variant that is first compiled. */
|
||||
GLProgram *main_program_ = nullptr;
|
||||
|
||||
/* When true, the shader generates its GLSources but it's not compiled.
|
||||
* (Used for subprocess compilation) */
|
||||
bool is_codegen_only_ = false;
|
||||
|
||||
/**
|
||||
* When the shader uses Specialization Constants these attribute contains the sources to
|
||||
* rebuild shader stages. When Specialization Constants aren't used they are empty to
|
||||
* reduce memory needs.
|
||||
*/
|
||||
GLSources vertex_sources_;
|
||||
GLSources geometry_sources_;
|
||||
GLSources fragment_sources_;
|
||||
GLSources compute_sources_;
|
||||
|
||||
Vector<const char *> specialization_constant_names_;
|
||||
|
||||
void update_program_and_sources(GLSources &stage_sources, MutableSpan<StringRefNull> sources);
|
||||
|
||||
/**
|
||||
* Return a GLProgram that reflects the given `constants_state`.
|
||||
* The returned program_id is in linked state, or an error happened during linking.
|
||||
*/
|
||||
GLShader::GLProgram &program_get(const shader::SpecializationConstants *constants_state);
|
||||
|
||||
/** True if any shader failed to compile. */
|
||||
bool compilation_failed_ = false;
|
||||
|
||||
std::string debug_source;
|
||||
|
||||
public:
|
||||
GLShader(const char *name);
|
||||
~GLShader();
|
||||
|
||||
void init(const shader::ShaderCreateInfo &info, bool is_codegen_only) override;
|
||||
|
||||
const shader::ShaderCreateInfo &patch_create_info(
|
||||
const shader::ShaderCreateInfo &original_info) override
|
||||
{
|
||||
return original_info;
|
||||
}
|
||||
|
||||
/** Return true on success. */
|
||||
void vertex_shader_from_glsl(const shader::ShaderCreateInfo &info,
|
||||
MutableSpan<StringRefNull> sources) override;
|
||||
void geometry_shader_from_glsl(const shader::ShaderCreateInfo &info,
|
||||
MutableSpan<StringRefNull> sources) override;
|
||||
void fragment_shader_from_glsl(const shader::ShaderCreateInfo &info,
|
||||
MutableSpan<StringRefNull> sources) override;
|
||||
void compute_shader_from_glsl(const shader::ShaderCreateInfo &info,
|
||||
MutableSpan<StringRefNull> sources) override;
|
||||
bool finalize(const shader::ShaderCreateInfo *info = nullptr) override;
|
||||
bool post_finalize(const shader::ShaderCreateInfo *info = nullptr);
|
||||
void warm_cache(int /*limit*/) override {};
|
||||
|
||||
std::string resources_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
std::string constants_declare(const shader::SpecializationConstants &constants_state) const;
|
||||
std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
|
||||
void bind(const shader::SpecializationConstants *constants_state) override;
|
||||
void unbind() override;
|
||||
|
||||
void uniform_float(int location, int comp_len, int array_size, const float *data) override;
|
||||
void uniform_int(int location, int comp_len, int array_size, const int *data) override;
|
||||
|
||||
bool is_compute() const
|
||||
{
|
||||
if (!vertex_sources_.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
if (!compute_sources_.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
return main_program_->compute_shader != 0;
|
||||
}
|
||||
|
||||
GLSourcesBaked get_sources();
|
||||
|
||||
private:
|
||||
StringRefNull glsl_patch_get(GLenum gl_stage);
|
||||
|
||||
bool has_specialization_constants() const
|
||||
{
|
||||
return constants->types.is_empty() == false;
|
||||
}
|
||||
|
||||
/** Create, compile and attach the shader stage to the shader program. */
|
||||
GLuint create_shader_stage(GLenum gl_stage,
|
||||
MutableSpan<StringRefNull> sources,
|
||||
GLSources &gl_sources,
|
||||
const shader::SpecializationConstants &constants_state);
|
||||
|
||||
/**
|
||||
* \brief features available on newer implementation such as native barycentric coordinates
|
||||
* and layered rendering, necessitate a geometry shader to work on older hardware.
|
||||
*/
|
||||
std::string workaround_geometry_shader_source_create(const shader::ShaderCreateInfo &info);
|
||||
|
||||
bool do_geometry_shader_injection(const shader::ShaderCreateInfo *info) const;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLShader");
|
||||
};
|
||||
|
||||
class GLShaderCompiler : public ShaderCompiler {
|
||||
public:
|
||||
GLShaderCompiler()
|
||||
: ShaderCompiler(GPU_max_parallel_compilations(), GPUWorker::ContextType::PerThread, true) {
|
||||
};
|
||||
|
||||
virtual void specialize_shader(const ShaderSpecialization &specialization) override;
|
||||
};
|
||||
|
||||
#if BLI_SUBPROCESS_SUPPORT
|
||||
|
||||
class GLCompilerWorker {
|
||||
friend class GLSubprocessShaderCompiler;
|
||||
|
||||
private:
|
||||
BlenderSubprocess subprocess_;
|
||||
std::unique_ptr<SharedMemory> shared_mem_;
|
||||
std::unique_ptr<SharedSemaphore> start_semaphore_;
|
||||
std::unique_ptr<SharedSemaphore> end_semaphore_;
|
||||
std::unique_ptr<SharedSemaphore> close_semaphore_;
|
||||
enum State {
|
||||
/* The worker has been acquired and the compilation has been requested. */
|
||||
COMPILATION_REQUESTED,
|
||||
/* The shader binary result is ready to be read. */
|
||||
COMPILATION_READY,
|
||||
/* The binary result has been loaded into a program and the worker can be released. */
|
||||
COMPILATION_FINISHED,
|
||||
/* The worker is not currently in use and can be acquired. */
|
||||
AVAILABLE
|
||||
};
|
||||
std::atomic<State> state_ = AVAILABLE;
|
||||
double compilation_start = 0;
|
||||
|
||||
GLCompilerWorker();
|
||||
~GLCompilerWorker();
|
||||
|
||||
void compile(const GLSourcesBaked &sources);
|
||||
bool block_until_ready();
|
||||
bool load_program_binary(GLint program);
|
||||
void release();
|
||||
|
||||
/* Check if the process may have closed/crashed/hanged. */
|
||||
bool is_lost();
|
||||
};
|
||||
|
||||
class GLSubprocessShaderCompiler : public ShaderCompiler {
|
||||
private:
|
||||
Vector<GLCompilerWorker *> workers_;
|
||||
std::mutex workers_mutex_;
|
||||
|
||||
GLCompilerWorker *get_compiler_worker();
|
||||
|
||||
GLShader::GLProgram *specialization_program_get(ShaderSpecialization &specialization);
|
||||
|
||||
public:
|
||||
GLSubprocessShaderCompiler()
|
||||
: ShaderCompiler(GPU_max_parallel_compilations(), GPUWorker::ContextType::PerThread, true) {
|
||||
};
|
||||
virtual ~GLSubprocessShaderCompiler() override;
|
||||
|
||||
virtual Shader *compile_shader(const shader::ShaderCreateInfo &info) override;
|
||||
virtual void specialize_shader(const ShaderSpecialization &specialization) override;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class GLSubprocessShaderCompiler : public ShaderCompiler {};
|
||||
|
||||
#endif
|
||||
|
||||
class GLLogParser : public GPULogParser {
|
||||
public:
|
||||
const char *parse_line(const char *source_combined,
|
||||
const char *log_line,
|
||||
GPULogItem &log_item) override;
|
||||
|
||||
protected:
|
||||
const char *skip_severity_prefix(const char *log_line, GPULogItem &log_item);
|
||||
const char *skip_severity_keyword(const char *log_line, GPULogItem &log_item);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLLogParser");
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
592
blender-5.2.0/source/blender/gpu/opengl/gl_shader_interface.cc
Normal file
592
blender-5.2.0/source/blender/gpu/opengl/gl_shader_interface.cc
Normal file
@@ -0,0 +1,592 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU shader interface (C --> GLSL)
|
||||
*/
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
|
||||
#include "gl_batch.hh"
|
||||
#include "gl_context.hh"
|
||||
|
||||
#include "gl_shader_interface.hh"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
using namespace blender::gpu::shader;
|
||||
namespace gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Binding assignment
|
||||
*
|
||||
* To mimic vulkan, we assign binding at shader creation to avoid shader recompilation.
|
||||
* In the future, we should set it in the shader using layout(binding = i) and query its value.
|
||||
* \{ */
|
||||
|
||||
static inline int block_binding(int32_t program, uint32_t block_index)
|
||||
{
|
||||
/* For now just assign a consecutive index. In the future, we should set it in
|
||||
* the shader using layout(binding = i) and query its value. */
|
||||
glUniformBlockBinding(program, block_index, block_index);
|
||||
return block_index;
|
||||
}
|
||||
|
||||
static inline int sampler_binding(int32_t program,
|
||||
uint32_t uniform_index,
|
||||
int32_t uniform_location,
|
||||
int *sampler_len)
|
||||
{
|
||||
/* Identify sampler uniforms and assign sampler units to them. */
|
||||
GLint type;
|
||||
glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type);
|
||||
|
||||
switch (type) {
|
||||
case GL_SAMPLER_1D:
|
||||
case GL_SAMPLER_2D:
|
||||
case GL_SAMPLER_3D:
|
||||
case GL_SAMPLER_CUBE:
|
||||
case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */
|
||||
case GL_SAMPLER_1D_SHADOW:
|
||||
case GL_SAMPLER_2D_SHADOW:
|
||||
case GL_SAMPLER_1D_ARRAY:
|
||||
case GL_SAMPLER_2D_ARRAY:
|
||||
case GL_SAMPLER_1D_ARRAY_SHADOW:
|
||||
case GL_SAMPLER_2D_ARRAY_SHADOW:
|
||||
case GL_SAMPLER_2D_MULTISAMPLE:
|
||||
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
case GL_SAMPLER_CUBE_SHADOW:
|
||||
case GL_SAMPLER_BUFFER:
|
||||
case GL_INT_SAMPLER_1D:
|
||||
case GL_INT_SAMPLER_2D:
|
||||
case GL_INT_SAMPLER_3D:
|
||||
case GL_INT_SAMPLER_CUBE:
|
||||
case GL_INT_SAMPLER_1D_ARRAY:
|
||||
case GL_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_INT_SAMPLER_2D_MULTISAMPLE:
|
||||
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
case GL_INT_SAMPLER_BUFFER:
|
||||
case GL_UNSIGNED_INT_SAMPLER_1D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_3D:
|
||||
case GL_UNSIGNED_INT_SAMPLER_CUBE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
|
||||
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
|
||||
case GL_UNSIGNED_INT_SAMPLER_BUFFER: {
|
||||
/* For now just assign a consecutive index. In the future, we should set it in
|
||||
* the shader using layout(binding = i) and query its value. */
|
||||
int binding = *sampler_len;
|
||||
glUniform1i(uniform_location, binding);
|
||||
(*sampler_len)++;
|
||||
return binding;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int image_binding(int32_t program,
|
||||
uint32_t uniform_index,
|
||||
int32_t uniform_location,
|
||||
int *image_len)
|
||||
{
|
||||
/* Identify image uniforms and assign image units to them. */
|
||||
GLint type;
|
||||
glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type);
|
||||
|
||||
switch (type) {
|
||||
case GL_IMAGE_1D:
|
||||
case GL_IMAGE_2D:
|
||||
case GL_IMAGE_3D:
|
||||
case GL_IMAGE_CUBE:
|
||||
case GL_IMAGE_BUFFER:
|
||||
case GL_IMAGE_1D_ARRAY:
|
||||
case GL_IMAGE_2D_ARRAY:
|
||||
case GL_IMAGE_CUBE_MAP_ARRAY:
|
||||
case GL_INT_IMAGE_1D:
|
||||
case GL_INT_IMAGE_2D:
|
||||
case GL_INT_IMAGE_3D:
|
||||
case GL_INT_IMAGE_CUBE:
|
||||
case GL_INT_IMAGE_BUFFER:
|
||||
case GL_INT_IMAGE_1D_ARRAY:
|
||||
case GL_INT_IMAGE_2D_ARRAY:
|
||||
case GL_INT_IMAGE_CUBE_MAP_ARRAY:
|
||||
case GL_UNSIGNED_INT_IMAGE_1D:
|
||||
case GL_UNSIGNED_INT_IMAGE_2D:
|
||||
case GL_UNSIGNED_INT_IMAGE_3D:
|
||||
case GL_UNSIGNED_INT_IMAGE_CUBE:
|
||||
case GL_UNSIGNED_INT_IMAGE_BUFFER:
|
||||
case GL_UNSIGNED_INT_IMAGE_1D_ARRAY:
|
||||
case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
|
||||
case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: {
|
||||
/* For now just assign a consecutive index. In the future, we should set it in
|
||||
* the shader using layout(binding = i) and query its value. */
|
||||
int binding = *image_len;
|
||||
glUniform1i(uniform_location, binding);
|
||||
(*image_len)++;
|
||||
return binding;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int ssbo_binding(int32_t program, uint32_t ssbo_index)
|
||||
{
|
||||
GLint binding = -1;
|
||||
GLenum property = GL_BUFFER_BINDING;
|
||||
GLint values_written = 0;
|
||||
glGetProgramResourceiv(
|
||||
program, GL_SHADER_STORAGE_BLOCK, ssbo_index, 1, &property, 1, &values_written, &binding);
|
||||
|
||||
return binding;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation / Destruction
|
||||
* \{ */
|
||||
|
||||
static Type gpu_type_from_gl_type(int gl_type)
|
||||
{
|
||||
switch (gl_type) {
|
||||
case GL_FLOAT:
|
||||
return Type::float_t;
|
||||
case GL_FLOAT_VEC2:
|
||||
return Type::float2_t;
|
||||
case GL_FLOAT_VEC3:
|
||||
return Type::float3_t;
|
||||
case GL_FLOAT_VEC4:
|
||||
return Type::float4_t;
|
||||
case GL_FLOAT_MAT3:
|
||||
return Type::float3x3_t;
|
||||
case GL_FLOAT_MAT4:
|
||||
return Type::float4x4_t;
|
||||
case GL_UNSIGNED_INT:
|
||||
return Type::uint_t;
|
||||
case GL_UNSIGNED_INT_VEC2:
|
||||
return Type::uint2_t;
|
||||
case GL_UNSIGNED_INT_VEC3:
|
||||
return Type::uint3_t;
|
||||
case GL_UNSIGNED_INT_VEC4:
|
||||
return Type::uint4_t;
|
||||
case GL_INT:
|
||||
return Type::int_t;
|
||||
case GL_INT_VEC2:
|
||||
return Type::int2_t;
|
||||
case GL_INT_VEC3:
|
||||
return Type::int3_t;
|
||||
case GL_INT_VEC4:
|
||||
return Type::int4_t;
|
||||
case GL_BOOL:
|
||||
return Type::bool_t;
|
||||
case GL_FLOAT_MAT2:
|
||||
case GL_FLOAT_MAT2x3:
|
||||
case GL_FLOAT_MAT2x4:
|
||||
case GL_FLOAT_MAT3x2:
|
||||
case GL_FLOAT_MAT3x4:
|
||||
case GL_FLOAT_MAT4x2:
|
||||
case GL_FLOAT_MAT4x3:
|
||||
default:
|
||||
BLI_assert(0);
|
||||
}
|
||||
return Type::float_t;
|
||||
}
|
||||
|
||||
GLShaderInterface::GLShaderInterface(GLuint program)
|
||||
{
|
||||
GLuint last_program;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast<GLint *>(&last_program));
|
||||
|
||||
/* Necessary to make #glUniform works. */
|
||||
glUseProgram(program);
|
||||
|
||||
GLint max_attr_name_len = 0, attr_len = 0;
|
||||
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len);
|
||||
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len);
|
||||
|
||||
GLint max_ubo_name_len = 0, ubo_len = 0;
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len);
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len);
|
||||
|
||||
GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0;
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len);
|
||||
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len);
|
||||
uniform_len = active_uniform_len;
|
||||
|
||||
GLint max_ssbo_name_len = 0, ssbo_len = 0;
|
||||
glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &ssbo_len);
|
||||
glGetProgramInterfaceiv(
|
||||
program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_len);
|
||||
|
||||
BLI_assert_msg(ubo_len <= 16, "enabled_ubo_mask_ is uint16_t");
|
||||
|
||||
/* Work around driver bug with Intel HD 4600 on Windows 7/8, where
|
||||
* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */
|
||||
if (attr_len > 0 && max_attr_name_len == 0) {
|
||||
max_attr_name_len = 256;
|
||||
}
|
||||
if (ubo_len > 0 && max_ubo_name_len == 0) {
|
||||
max_ubo_name_len = 256;
|
||||
}
|
||||
if (uniform_len > 0 && max_uniform_name_len == 0) {
|
||||
max_uniform_name_len = 256;
|
||||
}
|
||||
if (ssbo_len > 0 && max_ssbo_name_len == 0) {
|
||||
max_ssbo_name_len = 256;
|
||||
}
|
||||
|
||||
/* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before
|
||||
* allocating the uniform array. */
|
||||
GLint max_ubo_uni_len = 0;
|
||||
for (int i = 0; i < ubo_len; i++) {
|
||||
GLint ubo_uni_len;
|
||||
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len);
|
||||
max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len);
|
||||
uniform_len -= ubo_uni_len;
|
||||
}
|
||||
/* Bit set to true if uniform comes from a uniform block. */
|
||||
BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__);
|
||||
/* Set uniforms from block for exclusion. */
|
||||
GLint *ubo_uni_ids = MEM_new_array_uninitialized<GLint>(max_ubo_uni_len, __func__);
|
||||
for (int i = 0; i < ubo_len; i++) {
|
||||
GLint ubo_uni_len;
|
||||
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len);
|
||||
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids);
|
||||
for (int u = 0; u < ubo_uni_len; u++) {
|
||||
BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]);
|
||||
}
|
||||
}
|
||||
MEM_delete(ubo_uni_ids);
|
||||
|
||||
int input_tot_len = attr_len + ubo_len + uniform_len + ssbo_len;
|
||||
inputs_ = MEM_new_array_zeroed<ShaderInput>(input_tot_len, __func__);
|
||||
|
||||
const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len +
|
||||
uniform_len * max_uniform_name_len +
|
||||
ssbo_len * max_ssbo_name_len;
|
||||
name_buffer_ = MEM_new_array_uninitialized<char>(name_buffer_len, "name_buffer");
|
||||
uint32_t name_buffer_offset = 0;
|
||||
|
||||
/* Attributes */
|
||||
enabled_attr_mask_ = 0;
|
||||
for (int i = 0; i < attr_len; i++) {
|
||||
char *name = name_buffer_ + name_buffer_offset;
|
||||
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
|
||||
GLsizei name_len = 0;
|
||||
GLenum type;
|
||||
GLint size;
|
||||
|
||||
glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name);
|
||||
GLint location = glGetAttribLocation(program, name);
|
||||
/* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
|
||||
if (location == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ShaderInput *input = &inputs_[attr_len_++];
|
||||
input->location = input->binding = location;
|
||||
|
||||
name_buffer_offset += set_input_name(input, name, name_len);
|
||||
enabled_attr_mask_ |= (1 << input->location);
|
||||
|
||||
/* Used in `GPU_shader_get_attribute_info`. */
|
||||
attr_types_[input->location] = uint8_t(gpu_type_from_gl_type(type));
|
||||
}
|
||||
|
||||
/* Uniform Blocks */
|
||||
for (int i = 0; i < ubo_len; i++) {
|
||||
char *name = name_buffer_ + name_buffer_offset;
|
||||
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
|
||||
GLsizei name_len = 0;
|
||||
|
||||
glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name);
|
||||
|
||||
ShaderInput *input = &inputs_[attr_len_ + ubo_len_++];
|
||||
input->binding = input->location = block_binding(program, i);
|
||||
|
||||
name_buffer_offset += this->set_input_name(input, name, name_len);
|
||||
enabled_ubo_mask_ |= (1 << input->binding);
|
||||
}
|
||||
|
||||
/* Uniforms & samplers & images */
|
||||
for (int i = 0, sampler = 0, image = 0; i < active_uniform_len; i++) {
|
||||
if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) {
|
||||
continue;
|
||||
}
|
||||
char *name = name_buffer_ + name_buffer_offset;
|
||||
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
|
||||
GLsizei name_len = 0;
|
||||
|
||||
glGetActiveUniformName(program, i, remaining_buffer, &name_len, name);
|
||||
|
||||
ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_++];
|
||||
input->location = glGetUniformLocation(program, name);
|
||||
input->binding = sampler_binding(program, i, input->location, &sampler);
|
||||
|
||||
name_buffer_offset += this->set_input_name(input, name, name_len);
|
||||
enabled_tex_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
|
||||
|
||||
if (input->binding == -1) {
|
||||
input->binding = image_binding(program, i, input->location, &image);
|
||||
|
||||
enabled_ima_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
|
||||
}
|
||||
}
|
||||
|
||||
/* SSBOs */
|
||||
for (int i = 0; i < ssbo_len; i++) {
|
||||
char *name = name_buffer_ + name_buffer_offset;
|
||||
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
|
||||
GLsizei name_len = 0;
|
||||
glGetProgramResourceName(
|
||||
program, GL_SHADER_STORAGE_BLOCK, i, remaining_buffer, &name_len, name);
|
||||
|
||||
const GLint binding = ssbo_binding(program, i);
|
||||
|
||||
ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_++];
|
||||
input->binding = input->location = binding;
|
||||
|
||||
name_buffer_offset += this->set_input_name(input, name, name_len);
|
||||
enabled_ssbo_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
|
||||
}
|
||||
|
||||
/* Builtin Uniforms */
|
||||
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
|
||||
GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
|
||||
builtins_[u] = glGetUniformLocation(program, builtin_uniform_name(u));
|
||||
}
|
||||
|
||||
/* Builtin Uniforms Blocks */
|
||||
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) {
|
||||
GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int);
|
||||
const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u));
|
||||
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
|
||||
}
|
||||
|
||||
MEM_delete(uniforms_from_blocks);
|
||||
|
||||
/* Resize name buffer to save some memory. */
|
||||
if (name_buffer_offset < name_buffer_len) {
|
||||
name_buffer_ = static_cast<char *>(
|
||||
MEM_realloc_uninitialized(name_buffer_, name_buffer_offset));
|
||||
}
|
||||
|
||||
// this->debug_print();
|
||||
|
||||
this->sort_inputs();
|
||||
|
||||
glUseProgram(last_program);
|
||||
}
|
||||
|
||||
GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateInfo &info)
|
||||
{
|
||||
using namespace blender::gpu::shader;
|
||||
|
||||
attr_len_ = info.vertex_inputs_.size();
|
||||
uniform_len_ = info.push_constants_.size();
|
||||
constant_len_ = info.specialization_constants_.size();
|
||||
ubo_len_ = 0;
|
||||
ssbo_len_ = 0;
|
||||
|
||||
Vector<ShaderCreateInfo::Resource> all_resources = info.resources_get_all_();
|
||||
|
||||
for (ShaderCreateInfo::Resource &res : all_resources) {
|
||||
switch (res.bind_type) {
|
||||
case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
|
||||
ubo_len_++;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
|
||||
ssbo_len_++;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::SAMPLER:
|
||||
uniform_len_++;
|
||||
break;
|
||||
case ShaderCreateInfo::Resource::BindType::IMAGE:
|
||||
uniform_len_++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_msg(ubo_len_ <= 16, "enabled_ubo_mask_ is uint16_t");
|
||||
|
||||
int input_tot_len = attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_ + constant_len_;
|
||||
inputs_ = MEM_new_array_zeroed<ShaderInput>(input_tot_len, __func__);
|
||||
ShaderInput *input = inputs_;
|
||||
|
||||
name_buffer_ = MEM_new_array_uninitialized<char>(info.interface_names_size_, "name_buffer");
|
||||
uint32_t name_buffer_offset = 0;
|
||||
|
||||
/* Necessary to make #glUniform works. TODO(fclem) Remove. */
|
||||
GLuint last_program;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast<GLint *>(&last_program));
|
||||
|
||||
glUseProgram(program);
|
||||
|
||||
/* Attributes */
|
||||
for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
|
||||
copy_input_name(input, attr.name, name_buffer_, name_buffer_offset);
|
||||
if (true || !GLContext::explicit_location_support) {
|
||||
input->location = input->binding = glGetAttribLocation(program, attr.name.c_str());
|
||||
}
|
||||
else {
|
||||
input->location = input->binding = attr.index;
|
||||
}
|
||||
if (input->location != -1) {
|
||||
enabled_attr_mask_ |= (1 << input->location);
|
||||
|
||||
/* Used in `GPU_shader_get_attribute_info`. */
|
||||
attr_types_[input->location] = uint8_t(attr.type);
|
||||
}
|
||||
|
||||
input++;
|
||||
}
|
||||
|
||||
/* Uniform Blocks */
|
||||
for (const ShaderCreateInfo::Resource &res : all_resources) {
|
||||
if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
|
||||
copy_input_name(input, res.uniformbuf.name, name_buffer_, name_buffer_offset);
|
||||
input->location = input->binding = res.slot;
|
||||
enabled_ubo_mask_ |= (1 << input->binding);
|
||||
input++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Uniforms & samplers & images */
|
||||
for (const ShaderCreateInfo::Resource &res : all_resources) {
|
||||
if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
|
||||
copy_input_name(input, res.sampler.name, name_buffer_, name_buffer_offset);
|
||||
/* Until we make use of explicit uniform location or eliminate all
|
||||
* sampler manually changing. */
|
||||
if (true || !GLContext::explicit_location_support) {
|
||||
input->location = glGetUniformLocation(program, res.sampler.name.c_str());
|
||||
glUniform1i(input->location, res.slot);
|
||||
}
|
||||
input->binding = res.slot;
|
||||
enabled_tex_mask_ |= (1ull << input->binding);
|
||||
input++;
|
||||
}
|
||||
else if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
|
||||
copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset);
|
||||
/* Until we make use of explicit uniform location. */
|
||||
if (true || !GLContext::explicit_location_support) {
|
||||
input->location = glGetUniformLocation(program, res.image.name.c_str());
|
||||
glUniform1i(input->location, res.slot);
|
||||
}
|
||||
input->binding = res.slot;
|
||||
enabled_ima_mask_ |= (1 << input->binding);
|
||||
input++;
|
||||
}
|
||||
}
|
||||
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);
|
||||
input->binding = -1;
|
||||
input++;
|
||||
}
|
||||
set_image_formats_from_info(info);
|
||||
|
||||
/* SSBOs */
|
||||
for (const ShaderCreateInfo::Resource &res : all_resources) {
|
||||
if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
|
||||
copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset);
|
||||
input->location = input->binding = res.slot;
|
||||
enabled_ssbo_mask_ |= (1 << input->binding);
|
||||
input++;
|
||||
}
|
||||
}
|
||||
|
||||
for (const ShaderCreateInfo::Resource &res : info.geometry_resources_) {
|
||||
if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
|
||||
ssbo_attr_mask_ |= (1 << res.slot);
|
||||
}
|
||||
else {
|
||||
BLI_assert_msg(0, "Resource type is not supported for Geometry frequency");
|
||||
}
|
||||
}
|
||||
|
||||
/* Constants */
|
||||
int constant_id = 0;
|
||||
for (const SpecializationConstant &constant : info.specialization_constants_) {
|
||||
copy_input_name(input, constant.name, name_buffer_, name_buffer_offset);
|
||||
input->location = constant_id++;
|
||||
input++;
|
||||
}
|
||||
|
||||
this->sort_inputs();
|
||||
|
||||
/* Resolving builtins must happen after the inputs have been sorted. */
|
||||
/* Builtin Uniforms */
|
||||
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
|
||||
GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
|
||||
const ShaderInput *uni = this->uniform_get(builtin_uniform_name(u));
|
||||
builtins_[u] = (uni != nullptr) ? uni->location : -1;
|
||||
}
|
||||
|
||||
/* Builtin Uniforms Blocks */
|
||||
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) {
|
||||
GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int);
|
||||
const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u));
|
||||
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
|
||||
}
|
||||
|
||||
// this->debug_print();
|
||||
|
||||
glUseProgram(last_program);
|
||||
}
|
||||
|
||||
GLShaderInterface::~GLShaderInterface()
|
||||
{
|
||||
for (auto *ref : refs_) {
|
||||
if (ref != nullptr) {
|
||||
ref->remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Batch Reference
|
||||
* \{ */
|
||||
|
||||
void GLShaderInterface::ref_add(GLVaoCache *ref)
|
||||
{
|
||||
for (int i = 0; i < refs_.size(); i++) {
|
||||
if (refs_[i] == nullptr) {
|
||||
refs_[i] = ref;
|
||||
return;
|
||||
}
|
||||
}
|
||||
refs_.append(ref);
|
||||
}
|
||||
|
||||
void GLShaderInterface::ref_remove(GLVaoCache *ref)
|
||||
{
|
||||
for (int i = 0; i < refs_.size(); i++) {
|
||||
if (refs_[i] == ref) {
|
||||
refs_[i] = nullptr;
|
||||
break; /* cannot have duplicates */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Validation
|
||||
* TODO
|
||||
* \{ */
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPU shader interface (C --> GLSL)
|
||||
*
|
||||
* Structure detailing needed vertex inputs and resources for a specific shader.
|
||||
* A shader interface can be shared between two similar shaders.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_shader_interface.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLVaoCache;
|
||||
|
||||
/**
|
||||
* Implementation of Shader interface using OpenGL.
|
||||
*/
|
||||
class GLShaderInterface : public ShaderInterface {
|
||||
private:
|
||||
/** Reference to VaoCaches using this interface */
|
||||
Vector<GLVaoCache *> refs_;
|
||||
|
||||
public:
|
||||
GLShaderInterface(GLuint program, const shader::ShaderCreateInfo &info);
|
||||
GLShaderInterface(GLuint program);
|
||||
~GLShaderInterface();
|
||||
|
||||
void ref_add(GLVaoCache *ref);
|
||||
void ref_remove(GLVaoCache *ref);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLShaderInterface");
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
95
blender-5.2.0/source/blender/gpu/opengl/gl_shader_log.cc
Normal file
95
blender-5.2.0/source/blender/gpu/opengl/gl_shader_log.cc
Normal file
@@ -0,0 +1,95 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "gl_shader.hh"
|
||||
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
const char *GLLogParser::parse_line(const char *source_combined,
|
||||
const char *log_line,
|
||||
GPULogItem &log_item)
|
||||
{
|
||||
/* Skip ERROR: or WARNING:. */
|
||||
log_line = skip_severity_prefix(log_line, log_item);
|
||||
log_line = skip_separators(log_line, "(: ");
|
||||
|
||||
/* Parse error line & char numbers. */
|
||||
if (at_number(log_line)) {
|
||||
const char *error_line_number_end;
|
||||
log_item.cursor.row = parse_number(log_line, &error_line_number_end);
|
||||
/* Try to fetch the error character (not always available). */
|
||||
if (at_any(error_line_number_end, "(:") && at_number(&error_line_number_end[1])) {
|
||||
log_item.cursor.column = parse_number(error_line_number_end + 1, &log_line);
|
||||
}
|
||||
else {
|
||||
log_line = error_line_number_end;
|
||||
}
|
||||
/* There can be a 3rd number (case of mesa driver). */
|
||||
if (at_any(log_line, "(:") && at_number(&log_line[1])) {
|
||||
log_item.cursor.source = log_item.cursor.row;
|
||||
log_item.cursor.row = log_item.cursor.column;
|
||||
log_item.cursor.column = parse_number(log_line + 1, &error_line_number_end);
|
||||
log_line = error_line_number_end;
|
||||
}
|
||||
}
|
||||
|
||||
if ((log_item.cursor.row != -1) && (log_item.cursor.column != -1)) {
|
||||
if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) {
|
||||
/* source:row */
|
||||
log_item.cursor.source = log_item.cursor.row;
|
||||
log_item.cursor.row = log_item.cursor.column;
|
||||
log_item.cursor.column = -1;
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL)) {
|
||||
/* source:row */
|
||||
log_item.cursor.source = log_item.cursor.row;
|
||||
log_item.cursor.row = log_item.cursor.column;
|
||||
log_item.cursor.column = -1;
|
||||
}
|
||||
else {
|
||||
/* line:char */
|
||||
}
|
||||
}
|
||||
|
||||
if (log_item.cursor.row != -1) {
|
||||
/* Get to the wanted line. */
|
||||
size_t line_start_character = line_start_get(source_combined, log_item.cursor.row);
|
||||
if (line_start_character != -1) {
|
||||
StringRef filename = filename_get(source_combined, line_start_character);
|
||||
size_t line_number = source_line_get(source_combined, line_start_character);
|
||||
log_item.cursor.file_name_and_error_line = std::string(filename) + ':' +
|
||||
std::to_string(line_number);
|
||||
if (log_item.cursor.column != -1) {
|
||||
log_item.cursor.file_name_and_error_line += ':' +
|
||||
std::to_string(log_item.cursor.column + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_line = skip_separators(log_line, ":) ");
|
||||
|
||||
/* Skip to message. Avoid redundant info. */
|
||||
log_line = skip_severity_keyword(log_line, log_item);
|
||||
log_line = skip_separators(log_line, ":) ");
|
||||
|
||||
return log_line;
|
||||
}
|
||||
|
||||
const char *GLLogParser::skip_severity_prefix(const char *log_line, GPULogItem &log_item)
|
||||
{
|
||||
return skip_severity(log_line, log_item, "ERROR", "WARNING", "NOTE");
|
||||
}
|
||||
|
||||
const char *GLLogParser::skip_severity_keyword(const char *log_line, GPULogItem &log_item)
|
||||
{
|
||||
return skip_severity(log_line, log_item, "error", "warning", "note");
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
685
blender-5.2.0/source/blender/gpu/opengl/gl_state.cc
Normal file
685
blender-5.2.0/source/blender/gpu/opengl/gl_state.cc
Normal file
@@ -0,0 +1,685 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_bits.h"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "gl_context.hh"
|
||||
#include "gl_framebuffer.hh"
|
||||
#include "gl_texture.hh"
|
||||
|
||||
#include "gl_state.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GLStateManager
|
||||
* \{ */
|
||||
|
||||
GLStateManager::GLStateManager()
|
||||
{
|
||||
/* Set other states that never change. */
|
||||
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
glDisable(GL_DITHER);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
|
||||
/* Takes precedence over #GL_PRIMITIVE_RESTART.
|
||||
* Sets restart index correctly following the IBO type. */
|
||||
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
|
||||
|
||||
/* Limits. */
|
||||
glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, line_width_range_);
|
||||
|
||||
/* Force update using default state. */
|
||||
current_ = ~state;
|
||||
current_mutable_ = ~mutable_state;
|
||||
set_state(state);
|
||||
set_mutable_state(mutable_state);
|
||||
}
|
||||
|
||||
void GLStateManager::apply_state()
|
||||
{
|
||||
this->set_state(this->state);
|
||||
this->set_mutable_state(this->mutable_state);
|
||||
this->texture_bind_apply();
|
||||
this->image_bind_apply();
|
||||
|
||||
/* This is needed by gpu_py_offscreen. */
|
||||
active_fb->apply_state();
|
||||
};
|
||||
|
||||
void GLStateManager::force_state()
|
||||
{
|
||||
/* Little exception for clip distances since they need to keep the old count correct. */
|
||||
uint32_t clip_distances = current_.clip_distances;
|
||||
current_ = ~this->state;
|
||||
current_.clip_distances = clip_distances;
|
||||
current_mutable_ = ~this->mutable_state;
|
||||
this->set_state(this->state);
|
||||
this->set_mutable_state(this->mutable_state);
|
||||
};
|
||||
|
||||
void GLStateManager::set_state(const GPUState &state)
|
||||
{
|
||||
GPUState changed = state ^ current_;
|
||||
|
||||
if (changed.blend != 0) {
|
||||
set_blend(GPUBlend(state.blend));
|
||||
}
|
||||
if (changed.write_mask != 0) {
|
||||
set_write_mask(GPUWriteMask(state.write_mask));
|
||||
}
|
||||
if (changed.depth_test != 0) {
|
||||
set_depth_test(GPUDepthTest(state.depth_test));
|
||||
}
|
||||
if (changed.stencil_test != 0 || changed.stencil_op != 0) {
|
||||
set_stencil_test(GPUStencilTest(state.stencil_test), GPUStencilOp(state.stencil_op));
|
||||
set_stencil_mask(GPUStencilTest(state.stencil_test), mutable_state);
|
||||
}
|
||||
if (changed.clip_distances != 0) {
|
||||
set_clip_distances(state.clip_distances, current_.clip_distances);
|
||||
}
|
||||
if (changed.culling_test != 0) {
|
||||
set_backface_culling(GPUFaceCullTest(state.culling_test));
|
||||
}
|
||||
if (changed.logic_op_xor != 0) {
|
||||
set_logic_op(state.logic_op_xor);
|
||||
}
|
||||
if (changed.invert_facing != 0) {
|
||||
set_facing(state.invert_facing);
|
||||
}
|
||||
if (changed.provoking_vert != 0) {
|
||||
set_provoking_vert(GPUProvokingVertex(state.provoking_vert));
|
||||
}
|
||||
if (changed.clip_control != 0) {
|
||||
set_clip_control(state.clip_control);
|
||||
}
|
||||
|
||||
/* TODO: remove. */
|
||||
if (changed.polygon_smooth) {
|
||||
if (state.polygon_smooth) {
|
||||
glEnable(GL_POLYGON_SMOOTH);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_POLYGON_SMOOTH);
|
||||
}
|
||||
}
|
||||
if (changed.line_smooth) {
|
||||
if (state.line_smooth) {
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_LINE_SMOOTH);
|
||||
}
|
||||
}
|
||||
|
||||
current_ = state;
|
||||
}
|
||||
|
||||
void GLStateManager::set_mutable_state(const GPUStateMutable &state)
|
||||
{
|
||||
GPUStateMutable changed = state ^ current_mutable_;
|
||||
|
||||
/* TODO: remove, should be uniform. */
|
||||
if (float_as_uint(changed.point_size) != 0) {
|
||||
if (state.point_size > 0.0f) {
|
||||
glEnable(GL_PROGRAM_POINT_SIZE);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_PROGRAM_POINT_SIZE);
|
||||
glPointSize(fabsf(state.point_size));
|
||||
}
|
||||
}
|
||||
|
||||
if (float_as_uint(changed.line_width) != 0) {
|
||||
/* TODO: remove, should use wide line shader. */
|
||||
glLineWidth(clamp_f(state.line_width, line_width_range_[0], line_width_range_[1]));
|
||||
}
|
||||
|
||||
if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 ||
|
||||
changed.stencil_write_mask != 0)
|
||||
{
|
||||
set_stencil_mask(GPUStencilTest(current_.stencil_test), state);
|
||||
}
|
||||
|
||||
current_mutable_ = state;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name State set functions
|
||||
* \{ */
|
||||
|
||||
void GLStateManager::set_write_mask(const GPUWriteMask value)
|
||||
{
|
||||
glDepthMask((value & GPU_WRITE_DEPTH) != 0);
|
||||
glColorMask((value & GPU_WRITE_RED) != 0,
|
||||
(value & GPU_WRITE_GREEN) != 0,
|
||||
(value & GPU_WRITE_BLUE) != 0,
|
||||
(value & GPU_WRITE_ALPHA) != 0);
|
||||
|
||||
if (value == GPU_WRITE_NONE) {
|
||||
glEnable(GL_RASTERIZER_DISCARD);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_RASTERIZER_DISCARD);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStateManager::set_depth_test(const GPUDepthTest value)
|
||||
{
|
||||
GLenum func;
|
||||
switch (value) {
|
||||
case GPU_DEPTH_LESS:
|
||||
func = GL_LESS;
|
||||
break;
|
||||
case GPU_DEPTH_LESS_EQUAL:
|
||||
func = GL_LEQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_EQUAL:
|
||||
func = GL_EQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_GREATER:
|
||||
func = GL_GREATER;
|
||||
break;
|
||||
case GPU_DEPTH_GREATER_EQUAL:
|
||||
func = GL_GEQUAL;
|
||||
break;
|
||||
case GPU_DEPTH_ALWAYS:
|
||||
default:
|
||||
func = GL_ALWAYS;
|
||||
break;
|
||||
}
|
||||
|
||||
if (value != GPU_DEPTH_NONE) {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(func);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStateManager::set_stencil_test(const GPUStencilTest test, const GPUStencilOp operation)
|
||||
{
|
||||
switch (operation) {
|
||||
case GPU_STENCIL_OP_REPLACE:
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
break;
|
||||
case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
|
||||
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP);
|
||||
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP);
|
||||
break;
|
||||
case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
|
||||
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
|
||||
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
|
||||
break;
|
||||
case GPU_STENCIL_OP_NONE:
|
||||
default:
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
}
|
||||
|
||||
if (test != GPU_STENCIL_NONE) {
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStateManager::set_stencil_mask(const GPUStencilTest test, const GPUStateMutable &state)
|
||||
{
|
||||
GLenum func;
|
||||
switch (test) {
|
||||
case GPU_STENCIL_NEQUAL:
|
||||
func = GL_NOTEQUAL;
|
||||
break;
|
||||
case GPU_STENCIL_EQUAL:
|
||||
func = GL_EQUAL;
|
||||
break;
|
||||
case GPU_STENCIL_ALWAYS:
|
||||
func = GL_ALWAYS;
|
||||
break;
|
||||
case GPU_STENCIL_NONE:
|
||||
default:
|
||||
glStencilMask(0x00);
|
||||
glStencilFunc(GL_ALWAYS, 0x00, 0x00);
|
||||
return;
|
||||
}
|
||||
|
||||
glStencilMask(state.stencil_write_mask);
|
||||
glStencilFunc(func, state.stencil_reference, state.stencil_compare_mask);
|
||||
}
|
||||
|
||||
void GLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len)
|
||||
{
|
||||
for (int i = 0; i < new_dist_len; i++) {
|
||||
glEnable(GL_CLIP_DISTANCE0 + i);
|
||||
}
|
||||
for (int i = new_dist_len; i < old_dist_len; i++) {
|
||||
glDisable(GL_CLIP_DISTANCE0 + i);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStateManager::set_logic_op(const bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
glEnable(GL_COLOR_LOGIC_OP);
|
||||
glLogicOp(GL_XOR);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStateManager::set_facing(const bool invert)
|
||||
{
|
||||
glFrontFace((invert) ? GL_CW : GL_CCW);
|
||||
}
|
||||
|
||||
void GLStateManager::set_backface_culling(const GPUFaceCullTest test)
|
||||
{
|
||||
if (test != GPU_CULL_NONE) {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace((test == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStateManager::set_provoking_vert(const GPUProvokingVertex vert)
|
||||
{
|
||||
GLenum value = (vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION :
|
||||
GL_LAST_VERTEX_CONVENTION;
|
||||
glProvokingVertex(value);
|
||||
}
|
||||
|
||||
void GLStateManager::set_clip_control(const bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
/* Match Vulkan and Metal by default. */
|
||||
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
|
||||
}
|
||||
else {
|
||||
glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStateManager::set_blend(const GPUBlend value)
|
||||
{
|
||||
/**
|
||||
* Factors to the equation.
|
||||
* SRC is fragment shader output.
|
||||
* DST is frame-buffer color.
|
||||
* final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb;
|
||||
* final.a = SRC.a * src_alpha + DST.a * dst_alpha;
|
||||
*/
|
||||
GLenum src_rgb, src_alpha, dst_rgb, dst_alpha;
|
||||
switch (value) {
|
||||
default:
|
||||
case GPU_BLEND_ALPHA: {
|
||||
src_rgb = GL_SRC_ALPHA;
|
||||
dst_rgb = GL_ONE_MINUS_SRC_ALPHA;
|
||||
src_alpha = GL_ONE;
|
||||
dst_alpha = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_ALPHA_PREMULT: {
|
||||
src_rgb = GL_ONE;
|
||||
dst_rgb = GL_ONE_MINUS_SRC_ALPHA;
|
||||
src_alpha = GL_ONE;
|
||||
dst_alpha = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_ADDITIVE: {
|
||||
/* Do not let alpha accumulate but pre-multiply the source RGB by it. */
|
||||
src_rgb = GL_SRC_ALPHA;
|
||||
dst_rgb = GL_ONE;
|
||||
src_alpha = GL_ZERO;
|
||||
dst_alpha = GL_ONE;
|
||||
break;
|
||||
}
|
||||
/* Factors are not use in min or max mode, but avoid uninitialized values. */;
|
||||
case GPU_BLEND_MIN:
|
||||
case GPU_BLEND_MAX:
|
||||
case GPU_BLEND_SUBTRACT:
|
||||
case GPU_BLEND_ADDITIVE_PREMULT: {
|
||||
/* Let alpha accumulate. */
|
||||
src_rgb = GL_ONE;
|
||||
dst_rgb = GL_ONE;
|
||||
src_alpha = GL_ONE;
|
||||
dst_alpha = GL_ONE;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_MULTIPLY: {
|
||||
src_rgb = GL_DST_COLOR;
|
||||
dst_rgb = GL_ZERO;
|
||||
src_alpha = GL_DST_ALPHA;
|
||||
dst_alpha = GL_ZERO;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_INVERT: {
|
||||
src_rgb = GL_ONE_MINUS_DST_COLOR;
|
||||
dst_rgb = GL_ZERO;
|
||||
src_alpha = GL_ZERO;
|
||||
dst_alpha = GL_ONE;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_OIT: {
|
||||
src_rgb = GL_ONE;
|
||||
dst_rgb = GL_ONE;
|
||||
src_alpha = GL_ZERO;
|
||||
dst_alpha = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_BACKGROUND: {
|
||||
src_rgb = GL_ONE_MINUS_DST_ALPHA;
|
||||
dst_rgb = GL_SRC_ALPHA;
|
||||
src_alpha = GL_ZERO;
|
||||
dst_alpha = GL_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_ALPHA_UNDER_PREMUL: {
|
||||
src_rgb = GL_ONE_MINUS_DST_ALPHA;
|
||||
dst_rgb = GL_ONE;
|
||||
src_alpha = GL_ONE_MINUS_DST_ALPHA;
|
||||
dst_alpha = GL_ONE;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_CUSTOM: {
|
||||
src_rgb = GL_ONE;
|
||||
dst_rgb = GL_SRC1_COLOR;
|
||||
src_alpha = GL_ONE;
|
||||
dst_alpha = GL_SRC1_ALPHA;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_OVERLAY_MASK_FROM_ALPHA: {
|
||||
src_rgb = GL_ZERO;
|
||||
dst_rgb = GL_ONE_MINUS_SRC_ALPHA;
|
||||
src_alpha = GL_ZERO;
|
||||
dst_alpha = GL_ONE_MINUS_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
case GPU_BLEND_TRANSPARENCY: {
|
||||
src_rgb = GL_ONE;
|
||||
dst_rgb = GL_SRC_ALPHA;
|
||||
src_alpha = GL_ZERO;
|
||||
dst_alpha = GL_SRC_ALPHA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (value == GPU_BLEND_MIN) {
|
||||
glBlendEquation(GL_MIN);
|
||||
}
|
||||
else if (value == GPU_BLEND_MAX) {
|
||||
glBlendEquation(GL_MAX);
|
||||
}
|
||||
else if (value == GPU_BLEND_SUBTRACT) {
|
||||
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
|
||||
}
|
||||
else {
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
}
|
||||
|
||||
/* Always set the blend function. This avoid a rendering error when blending is disabled but
|
||||
* GPU_BLEND_CUSTOM was used just before and the frame-buffer is using more than 1 color target.
|
||||
*/
|
||||
glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha);
|
||||
if (value != GPU_BLEND_NONE) {
|
||||
glEnable(GL_BLEND);
|
||||
}
|
||||
else {
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Texture State Management
|
||||
* \{ */
|
||||
|
||||
void GLStateManager::texture_bind(Texture *tex_, GPUSamplerState sampler_state, int unit)
|
||||
{
|
||||
BLI_assert(unit < GPU_max_textures());
|
||||
GLTexture *tex = static_cast<GLTexture *>(tex_);
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
tex->check_feedback_loop();
|
||||
}
|
||||
/* Eliminate redundant binds. */
|
||||
if ((textures_[unit] == tex->tex_id_) &&
|
||||
(samplers_[unit] == GLTexture::get_sampler(sampler_state)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
targets_[unit] = tex->target_;
|
||||
textures_[unit] = tex->tex_id_;
|
||||
samplers_[unit] = GLTexture::get_sampler(sampler_state);
|
||||
tex->is_bound_ = true;
|
||||
dirty_texture_binds_ |= 1ULL << unit;
|
||||
}
|
||||
|
||||
void GLStateManager::texture_bind_temp(GLTexture *tex)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(tex->target_, tex->tex_id_);
|
||||
/* Will reset the first texture that was originally bound to slot 0 back before drawing. */
|
||||
dirty_texture_binds_ |= 1ULL;
|
||||
/* NOTE: This might leave this texture attached to this target even after update.
|
||||
* In practice it is not causing problems as we have incorrect binding detection
|
||||
* at higher level. */
|
||||
}
|
||||
|
||||
void GLStateManager::texture_unbind(Texture *tex_)
|
||||
{
|
||||
GLTexture *tex = static_cast<GLTexture *>(tex_);
|
||||
if (!tex->is_bound_) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLuint tex_id = tex->tex_id_;
|
||||
for (int i = 0; i < ARRAY_SIZE(textures_); i++) {
|
||||
if (textures_[i] == tex_id) {
|
||||
textures_[i] = 0;
|
||||
samplers_[i] = 0;
|
||||
dirty_texture_binds_ |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
tex->is_bound_ = false;
|
||||
}
|
||||
|
||||
void GLStateManager::texture_unbind_all()
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(textures_); i++) {
|
||||
if (textures_[i] != 0) {
|
||||
textures_[i] = 0;
|
||||
samplers_[i] = 0;
|
||||
dirty_texture_binds_ |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
this->texture_bind_apply();
|
||||
}
|
||||
|
||||
void GLStateManager::texture_bind_apply()
|
||||
{
|
||||
if (dirty_texture_binds_ == 0) {
|
||||
return;
|
||||
}
|
||||
uint64_t dirty_bind = dirty_texture_binds_;
|
||||
dirty_texture_binds_ = 0;
|
||||
|
||||
int first = bitscan_forward_uint64(dirty_bind);
|
||||
int last = 64 - bitscan_reverse_uint64(dirty_bind);
|
||||
int count = last - first;
|
||||
|
||||
if (GLContext::multi_bind_support) {
|
||||
glBindTextures(first, count, textures_ + first);
|
||||
glBindSamplers(first, count, samplers_ + first);
|
||||
}
|
||||
else {
|
||||
for (int unit = first; unit < last; unit++) {
|
||||
if ((dirty_bind >> unit) & 1UL) {
|
||||
glActiveTexture(GL_TEXTURE0 + unit);
|
||||
glBindTexture(targets_[unit], textures_[unit]);
|
||||
glBindSampler(unit, samplers_[unit]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GLStateManager::bound_texture_slots()
|
||||
{
|
||||
uint64_t bound_slots = 0;
|
||||
for (int i = 0; i < ARRAY_SIZE(textures_); i++) {
|
||||
if (textures_[i] != 0) {
|
||||
bound_slots |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
return bound_slots;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Image Binding (from image load store)
|
||||
* \{ */
|
||||
|
||||
void GLStateManager::image_bind(Texture *tex_, int unit)
|
||||
{
|
||||
/* Minimum support is 8 image in the fragment shader. No image for other stages. */
|
||||
BLI_assert(unit < 8);
|
||||
GLTexture *tex = static_cast<GLTexture *>(tex_);
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
tex->check_feedback_loop();
|
||||
}
|
||||
images_[unit] = tex->tex_id_;
|
||||
formats_[unit] = to_gl_internal_format(tex->format_);
|
||||
image_formats[unit] = TextureWriteFormat(tex->format_get());
|
||||
tex->is_bound_image_ = true;
|
||||
dirty_image_binds_ |= 1ULL << unit;
|
||||
}
|
||||
|
||||
void GLStateManager::image_unbind(Texture *tex_)
|
||||
{
|
||||
GLTexture *tex = static_cast<GLTexture *>(tex_);
|
||||
if (!tex->is_bound_image_) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLuint tex_id = tex->tex_id_;
|
||||
for (int i = 0; i < ARRAY_SIZE(images_); i++) {
|
||||
if (images_[i] == tex_id) {
|
||||
images_[i] = 0;
|
||||
image_formats[i] = TextureWriteFormat::Invalid;
|
||||
dirty_image_binds_ |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
tex->is_bound_image_ = false;
|
||||
}
|
||||
|
||||
void GLStateManager::image_unbind_all()
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(images_); i++) {
|
||||
if (images_[i] != 0) {
|
||||
images_[i] = 0;
|
||||
dirty_image_binds_ |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
image_formats.fill(TextureWriteFormat::Invalid);
|
||||
this->image_bind_apply();
|
||||
}
|
||||
|
||||
void GLStateManager::image_bind_apply()
|
||||
{
|
||||
if (dirty_image_binds_ == 0) {
|
||||
return;
|
||||
}
|
||||
uint32_t dirty_bind = dirty_image_binds_;
|
||||
dirty_image_binds_ = 0;
|
||||
|
||||
int first = bitscan_forward_uint(dirty_bind);
|
||||
int last = 32 - bitscan_reverse_uint(dirty_bind);
|
||||
int count = last - first;
|
||||
|
||||
if (GLContext::multi_bind_image_support) {
|
||||
glBindImageTextures(first, count, images_ + first);
|
||||
}
|
||||
else {
|
||||
for (int unit = first; unit < last; unit++) {
|
||||
if ((dirty_bind >> unit) & 1UL) {
|
||||
glBindImageTexture(unit, images_[unit], 0, GL_TRUE, 0, GL_READ_WRITE, formats_[unit]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GLStateManager::bound_image_slots()
|
||||
{
|
||||
uint8_t bound_slots = 0;
|
||||
for (int i = 0; i < ARRAY_SIZE(images_); i++) {
|
||||
if (images_[i] != 0) {
|
||||
bound_slots |= 1ULL << i;
|
||||
}
|
||||
}
|
||||
return bound_slots;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Memory barrier
|
||||
* \{ */
|
||||
|
||||
void GLStateManager::issue_barrier(GPUBarrier barrier_bits)
|
||||
{
|
||||
glMemoryBarrier(to_gl(barrier_bits));
|
||||
}
|
||||
|
||||
GLFence::~GLFence()
|
||||
{
|
||||
if (gl_sync_ != nullptr) {
|
||||
glDeleteSync(gl_sync_);
|
||||
gl_sync_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GLFence::signal()
|
||||
{
|
||||
/* If fence is already signaled, create a newly signaled fence primitive. */
|
||||
if (gl_sync_) {
|
||||
glDeleteSync(gl_sync_);
|
||||
}
|
||||
|
||||
gl_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
signalled_ = true;
|
||||
}
|
||||
|
||||
void GLFence::wait()
|
||||
{
|
||||
/* Do not wait if fence does not yet exist. */
|
||||
if (gl_sync_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
glWaitSync(gl_sync_, 0, GL_TIMEOUT_IGNORED);
|
||||
signalled_ = false;
|
||||
}
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
156
blender-5.2.0/source/blender/gpu/opengl/gl_state.hh
Normal file
156
blender-5.2.0/source/blender/gpu/opengl/gl_state.hh
Normal file
@@ -0,0 +1,156 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "gpu_state_private.hh"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLFrameBuffer;
|
||||
class GLTexture;
|
||||
|
||||
/**
|
||||
* State manager keeping track of the draw state and applying it before drawing.
|
||||
* Opengl Implementation.
|
||||
*/
|
||||
class GLStateManager : public StateManager {
|
||||
public:
|
||||
/** Another reference to the active frame-buffer. */
|
||||
GLFrameBuffer *active_fb = nullptr;
|
||||
|
||||
private:
|
||||
/** Current state of the GL implementation. Avoids resetting the whole state for every change. */
|
||||
GPUState current_;
|
||||
GPUStateMutable current_mutable_;
|
||||
/** Limits. */
|
||||
float line_width_range_[2];
|
||||
|
||||
/**
|
||||
* Texture state:
|
||||
* We keep the full stack of textures and sampler bounds to use multi bind, and to be able to
|
||||
* edit and restore texture binds on the fly without querying the context.
|
||||
* Also this allows us to keep track of textures bounds to many texture units.
|
||||
* Keep the targets to know what target to set to 0 for unbinding (legacy).
|
||||
* Init first target to GL_TEXTURE_2D for texture_bind_temp to work.
|
||||
*/
|
||||
GLuint targets_[64] = {GL_TEXTURE_2D};
|
||||
GLuint textures_[64] = {0};
|
||||
GLuint samplers_[64] = {0};
|
||||
uint64_t dirty_texture_binds_ = 0;
|
||||
|
||||
GLuint images_[8] = {0};
|
||||
GLenum formats_[8] = {0};
|
||||
uint8_t dirty_image_binds_ = 0;
|
||||
|
||||
public:
|
||||
GLStateManager();
|
||||
|
||||
void apply_state() override;
|
||||
/**
|
||||
* Will set all the states regardless of the current ones.
|
||||
*/
|
||||
void force_state() override;
|
||||
|
||||
void issue_barrier(GPUBarrier barrier_bits) override;
|
||||
|
||||
void texture_bind(Texture *tex, GPUSamplerState sampler, int unit) override;
|
||||
/**
|
||||
* Bind the texture to slot 0 for editing purpose. Used by legacy pipeline.
|
||||
*/
|
||||
void texture_bind_temp(GLTexture *tex);
|
||||
void texture_unbind(Texture *tex) override;
|
||||
void texture_unbind_all() override;
|
||||
|
||||
void image_bind(Texture *tex, int unit) override;
|
||||
void image_unbind(Texture *tex) override;
|
||||
void image_unbind_all() override;
|
||||
|
||||
uint64_t bound_texture_slots();
|
||||
uint8_t bound_image_slots();
|
||||
|
||||
private:
|
||||
static void set_write_mask(GPUWriteMask value);
|
||||
static void set_depth_test(GPUDepthTest value);
|
||||
static void set_stencil_test(GPUStencilTest test, GPUStencilOp operation);
|
||||
static void set_stencil_mask(GPUStencilTest test, const GPUStateMutable &state);
|
||||
static void set_clip_distances(int new_dist_len, int old_dist_len);
|
||||
static void set_logic_op(bool enable);
|
||||
static void set_facing(bool invert);
|
||||
static void set_backface_culling(GPUFaceCullTest test);
|
||||
static void set_provoking_vert(GPUProvokingVertex vert);
|
||||
static void set_clip_control(bool enable);
|
||||
static void set_blend(GPUBlend value);
|
||||
|
||||
void set_state(const GPUState &state);
|
||||
void set_mutable_state(const GPUStateMutable &state);
|
||||
|
||||
void texture_bind_apply();
|
||||
void image_bind_apply();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLStateManager")
|
||||
};
|
||||
|
||||
/* Fence synchronization primitive. */
|
||||
class GLFence : public Fence {
|
||||
private:
|
||||
GLsync gl_sync_ = 0;
|
||||
|
||||
public:
|
||||
GLFence() : Fence() {};
|
||||
~GLFence();
|
||||
|
||||
void signal() override;
|
||||
void wait() override;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLFence")
|
||||
};
|
||||
|
||||
static inline GLbitfield to_gl(GPUBarrier barrier_bits)
|
||||
{
|
||||
GLbitfield barrier = 0;
|
||||
if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS) {
|
||||
barrier |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) {
|
||||
barrier |= GL_SHADER_STORAGE_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) {
|
||||
barrier |= GL_TEXTURE_FETCH_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_TEXTURE_UPDATE) {
|
||||
barrier |= GL_TEXTURE_UPDATE_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_COMMAND) {
|
||||
barrier |= GL_COMMAND_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_FRAMEBUFFER) {
|
||||
barrier |= GL_FRAMEBUFFER_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY) {
|
||||
barrier |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_ELEMENT_ARRAY) {
|
||||
barrier |= GL_ELEMENT_ARRAY_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_UNIFORM) {
|
||||
barrier |= GL_UNIFORM_BARRIER_BIT;
|
||||
}
|
||||
if (barrier_bits & GPU_BARRIER_BUFFER_UPDATE) {
|
||||
barrier |= GL_BUFFER_UPDATE_BARRIER_BIT;
|
||||
}
|
||||
return barrier;
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
262
blender-5.2.0/source/blender/gpu/opengl/gl_storage_buffer.cc
Normal file
262
blender-5.2.0/source/blender/gpu/opengl/gl_storage_buffer.cc
Normal file
@@ -0,0 +1,262 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "gl_backend.hh"
|
||||
#include "gl_debug.hh"
|
||||
#include "gl_storage_buffer.hh"
|
||||
#include "gl_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
GLStorageBuf::GLStorageBuf(size_t size, GPUUsageType usage, const char *name)
|
||||
: StorageBuf(size, name)
|
||||
{
|
||||
usage_ = usage;
|
||||
/* Do not create SSBO GL buffer here to allow allocation from any thread. */
|
||||
BLI_assert(size <= GPU_max_storage_buffer_size());
|
||||
}
|
||||
|
||||
GLStorageBuf::~GLStorageBuf()
|
||||
{
|
||||
if (read_fence_) {
|
||||
glDeleteSync(read_fence_);
|
||||
}
|
||||
|
||||
if (persistent_ptr_) {
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glUnmapNamedBuffer(read_ssbo_id_);
|
||||
}
|
||||
else {
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, read_ssbo_id_);
|
||||
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (read_ssbo_id_) {
|
||||
GLContext::buffer_free(read_ssbo_id_);
|
||||
}
|
||||
|
||||
GLContext::buffer_free(ssbo_id_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Data upload / update
|
||||
* \{ */
|
||||
|
||||
void GLStorageBuf::init()
|
||||
{
|
||||
BLI_assert(GLContext::get());
|
||||
|
||||
alloc_size_in_bytes_ = ceil_to_multiple_ul(size_in_bytes_, 16);
|
||||
glGenBuffers(1, &ssbo_id_);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, alloc_size_in_bytes_, nullptr, to_gl(this->usage_));
|
||||
|
||||
debug::object_label(GL_SHADER_STORAGE_BUFFER, ssbo_id_, name_);
|
||||
}
|
||||
|
||||
void GLStorageBuf::update(const void *data)
|
||||
{
|
||||
if (ssbo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_);
|
||||
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, size_in_bytes_, data);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Usage
|
||||
* \{ */
|
||||
|
||||
void GLStorageBuf::bind(int slot)
|
||||
{
|
||||
if (slot >= GLContext::max_ssbo_binds) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.\n",
|
||||
name_,
|
||||
slot,
|
||||
GLContext::max_ssbo_binds);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ssbo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
if (data_ != nullptr) {
|
||||
this->update(data_);
|
||||
MEM_SAFE_DELETE_VOID(data_);
|
||||
}
|
||||
|
||||
slot_ = slot;
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot_, ssbo_id_);
|
||||
|
||||
#ifndef NDEBUG
|
||||
BLI_assert(slot < 16);
|
||||
GLContext::get()->bound_ssbo_slots |= 1 << slot;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLStorageBuf::bind_as(GLenum target)
|
||||
{
|
||||
BLI_assert_msg(ssbo_id_ != 0,
|
||||
"Trying to use storage buffer as indirect buffer but buffer was never filled.");
|
||||
glBindBuffer(target, ssbo_id_);
|
||||
}
|
||||
|
||||
void GLStorageBuf::unbind()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
/* NOTE: This only unbinds the last bound slot. */
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot_, 0);
|
||||
/* Hope that the context did not change. */
|
||||
GLContext::get()->bound_ssbo_slots &= ~(1 << slot_);
|
||||
#endif
|
||||
slot_ = 0;
|
||||
}
|
||||
|
||||
void GLStorageBuf::clear(uint32_t clear_value)
|
||||
{
|
||||
if (ssbo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glClearNamedBufferData(ssbo_id_, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, &clear_value);
|
||||
}
|
||||
else {
|
||||
/* WATCH(@fclem): This should be ok since we only use clear outside of drawing functions. */
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_);
|
||||
glClearBufferData(
|
||||
GL_SHADER_STORAGE_BUFFER, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, &clear_value);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStorageBuf::copy_sub(VertBuf *src_, uint dst_offset, uint src_offset, uint copy_size)
|
||||
{
|
||||
GLVertBuf *src = static_cast<GLVertBuf *>(src_);
|
||||
GLStorageBuf *dst = this;
|
||||
|
||||
if (dst->ssbo_id_ == 0) {
|
||||
dst->init();
|
||||
}
|
||||
if (src->vbo_id_ == 0) {
|
||||
src->bind();
|
||||
}
|
||||
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glCopyNamedBufferSubData(src->vbo_id_, dst->ssbo_id_, src_offset, dst_offset, copy_size);
|
||||
}
|
||||
else {
|
||||
/* This binds the buffer to GL_ARRAY_BUFFER and upload the data if any. */
|
||||
src->bind();
|
||||
glBindBuffer(GL_COPY_WRITE_BUFFER, dst->ssbo_id_);
|
||||
glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, src_offset, dst_offset, copy_size);
|
||||
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GLStorageBuf::async_flush_to_host()
|
||||
{
|
||||
if (ssbo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
if (read_ssbo_id_ == 0) {
|
||||
glGenBuffers(1, &read_ssbo_id_);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, read_ssbo_id_);
|
||||
glBufferStorage(GL_SHADER_STORAGE_BUFFER,
|
||||
alloc_size_in_bytes_,
|
||||
nullptr,
|
||||
GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT);
|
||||
persistent_ptr_ = glMapBufferRange(GL_SHADER_STORAGE_BUFFER,
|
||||
0,
|
||||
alloc_size_in_bytes_,
|
||||
GL_MAP_PERSISTENT_BIT | GL_MAP_READ_BIT);
|
||||
BLI_assert(persistent_ptr_);
|
||||
debug::object_label(GL_SHADER_STORAGE_BUFFER, read_ssbo_id_, name_);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
}
|
||||
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glCopyNamedBufferSubData(ssbo_id_, read_ssbo_id_, 0, 0, alloc_size_in_bytes_);
|
||||
}
|
||||
else {
|
||||
glBindBuffer(GL_COPY_READ_BUFFER, ssbo_id_);
|
||||
glBindBuffer(GL_COPY_WRITE_BUFFER, read_ssbo_id_);
|
||||
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, alloc_size_in_bytes_);
|
||||
glBindBuffer(GL_COPY_READ_BUFFER, 0);
|
||||
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
|
||||
}
|
||||
|
||||
glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
|
||||
|
||||
if (read_fence_) {
|
||||
glDeleteSync(read_fence_);
|
||||
}
|
||||
read_fence_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
|
||||
void GLStorageBuf::read(void *data)
|
||||
{
|
||||
if (data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!read_fence_) {
|
||||
/* Synchronous path. */
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glGetNamedBufferSubData(ssbo_id_, 0, size_in_bytes_, data);
|
||||
}
|
||||
else {
|
||||
glBindBuffer(GL_COPY_READ_BUFFER, ssbo_id_);
|
||||
glGetBufferSubData(GL_COPY_READ_BUFFER, 0, size_in_bytes_, data);
|
||||
glBindBuffer(GL_COPY_READ_BUFFER, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (glClientWaitSync(read_fence_, GL_SYNC_FLUSH_COMMANDS_BIT, 1000) == GL_TIMEOUT_EXPIRED) {
|
||||
/* Repeat until the data is ready. */
|
||||
}
|
||||
glDeleteSync(read_fence_);
|
||||
read_fence_ = nullptr;
|
||||
|
||||
BLI_assert(persistent_ptr_);
|
||||
memcpy(data, persistent_ptr_, size_in_bytes_);
|
||||
}
|
||||
|
||||
void GLStorageBuf::sync_as_indirect_buffer()
|
||||
{
|
||||
bind_as(GL_DRAW_INDIRECT_BUFFER);
|
||||
glMemoryBarrier(GL_COMMAND_BARRIER_BIT);
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
58
blender-5.2.0/source/blender/gpu/opengl/gl_storage_buffer.hh
Normal file
58
blender-5.2.0/source/blender/gpu/opengl/gl_storage_buffer.hh
Normal file
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "gpu_storage_buffer_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/**
|
||||
* Implementation of Storage Buffers using OpenGL.
|
||||
*/
|
||||
class GLStorageBuf : public StorageBuf {
|
||||
private:
|
||||
/** Slot to which this UBO is currently bound. -1 if not bound. */
|
||||
int slot_ = -1;
|
||||
/** OpenGL Object handle. */
|
||||
GLuint ssbo_id_ = 0;
|
||||
/** Usage type. */
|
||||
GPUUsageType usage_ = GPUUsageType(-1);
|
||||
/* Read */
|
||||
GLuint read_ssbo_id_ = 0;
|
||||
GLsync read_fence_ = 0;
|
||||
void *persistent_ptr_ = nullptr;
|
||||
size_t alloc_size_in_bytes_ = 0;
|
||||
|
||||
public:
|
||||
GLStorageBuf(size_t size, GPUUsageType usage, const char *name);
|
||||
~GLStorageBuf();
|
||||
|
||||
void update(const void *data) override;
|
||||
void bind(int slot) override;
|
||||
void unbind() override;
|
||||
void clear(uint32_t clear_value) override;
|
||||
void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override;
|
||||
void read(void *data) override;
|
||||
void async_flush_to_host() override;
|
||||
void sync_as_indirect_buffer() override;
|
||||
|
||||
/* Special internal function to bind SSBOs to indirect argument targets. */
|
||||
void bind_as(GLenum target);
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLStorageBuf");
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
905
blender-5.2.0/source/blender/gpu/opengl/gl_texture.cc
Normal file
905
blender-5.2.0/source/blender/gpu/opengl/gl_texture.cc
Normal file
@@ -0,0 +1,905 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_math_half.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
#include "GPU_vertex_buffer.hh" /* TODO: should be `gl_vertex_buffer.hh`. */
|
||||
#include "MEM_guardedalloc.h"
|
||||
#include "gl_backend.hh"
|
||||
#include "gl_debug.hh"
|
||||
#include "gl_state.hh"
|
||||
|
||||
#include "gl_texture.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
GLTexture::GLTexture(const char *name) : Texture(name)
|
||||
{
|
||||
BLI_assert(GLContext::get() != nullptr);
|
||||
|
||||
glGenTextures(1, &tex_id_);
|
||||
}
|
||||
|
||||
GLTexture::~GLTexture()
|
||||
{
|
||||
if (framebuffer_) {
|
||||
GPU_framebuffer_free(framebuffer_);
|
||||
}
|
||||
GLContext *ctx = GLContext::get();
|
||||
if (ctx != nullptr && is_bound_) {
|
||||
/* This avoid errors when the texture is still inside the bound texture array. */
|
||||
ctx->state_manager->texture_unbind(this);
|
||||
ctx->state_manager->image_unbind(this);
|
||||
}
|
||||
GLContext::texture_free(tex_id_);
|
||||
}
|
||||
|
||||
bool GLTexture::init_internal()
|
||||
{
|
||||
target_ = to_gl_target(type_);
|
||||
|
||||
/* We need to bind once to define the texture type. */
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
|
||||
if (!this->proxy_check(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLenum internal_format = to_gl_internal_format(format_);
|
||||
const bool is_cubemap = bool(type_ == GPU_TEXTURE_CUBE);
|
||||
const int dimensions = (is_cubemap) ? 2 : this->dimensions_count();
|
||||
|
||||
switch (dimensions) {
|
||||
default:
|
||||
case 1:
|
||||
glTexStorage1D(target_, mipmaps_, internal_format, w_);
|
||||
break;
|
||||
case 2:
|
||||
glTexStorage2D(target_, mipmaps_, internal_format, w_, h_);
|
||||
break;
|
||||
case 3:
|
||||
glTexStorage3D(target_, mipmaps_, internal_format, w_, h_, d_);
|
||||
break;
|
||||
}
|
||||
this->mip_range_set(0, mipmaps_ - 1);
|
||||
|
||||
/* Avoid issue with formats not supporting filtering. Nearest by default. */
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glTextureParameteri(tex_id_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
else {
|
||||
glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
||||
debug::object_label(GL_TEXTURE, tex_id_, name_.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLTexture::init_internal(VertBuf *vbo)
|
||||
{
|
||||
GLVertBuf *gl_vbo = static_cast<GLVertBuf *>(vbo);
|
||||
target_ = to_gl_target(type_);
|
||||
|
||||
/* We need to bind once to define the texture type. */
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
|
||||
GLenum internal_format = to_gl_internal_format(format_);
|
||||
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glTextureBuffer(tex_id_, internal_format, gl_vbo->vbo_id_);
|
||||
}
|
||||
else {
|
||||
glTexBuffer(target_, internal_format, gl_vbo->vbo_id_);
|
||||
}
|
||||
|
||||
debug::object_label(GL_TEXTURE, tex_id_, name_.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLTexture::init_internal(gpu::Texture *src,
|
||||
int mip_offset,
|
||||
int layer_offset,
|
||||
bool use_stencil)
|
||||
{
|
||||
const GLTexture *gl_src = static_cast<const GLTexture *>(src);
|
||||
GLenum internal_format = to_gl_internal_format(format_);
|
||||
target_ = to_gl_target(type_);
|
||||
|
||||
glTextureView(tex_id_,
|
||||
target_,
|
||||
gl_src->tex_id_,
|
||||
internal_format,
|
||||
mip_offset,
|
||||
mipmaps_,
|
||||
layer_offset,
|
||||
this->layer_count());
|
||||
|
||||
debug::object_label(GL_TEXTURE, tex_id_, name_.c_str());
|
||||
|
||||
/* Stencil view support. */
|
||||
if (ELEM(format_, TextureFormat::SFLOAT_32_DEPTH_UINT_8)) {
|
||||
stencil_texture_mode_set(use_stencil);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Operations
|
||||
* \{ */
|
||||
|
||||
void GLTexture::update_sub_direct_state_access(
|
||||
int mip, int offset[3], int extent[3], GLenum format, GLenum type, const void *data)
|
||||
{
|
||||
if (format_flag_ & GPU_FORMAT_COMPRESSED) {
|
||||
size_t size = ((extent[0] + 3) / 4) * ((extent[1] + 3) / 4) * to_block_size(format_);
|
||||
switch (this->dimensions_count()) {
|
||||
default:
|
||||
case 1:
|
||||
glCompressedTextureSubImage1D(tex_id_, mip, offset[0], extent[0], format, size, data);
|
||||
break;
|
||||
case 2:
|
||||
glCompressedTextureSubImage2D(
|
||||
tex_id_, mip, UNPACK2(offset), UNPACK2(extent), format, size, data);
|
||||
break;
|
||||
case 3:
|
||||
glCompressedTextureSubImage3D(
|
||||
tex_id_, mip, UNPACK3(offset), UNPACK3(extent), format, size, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (this->dimensions_count()) {
|
||||
default:
|
||||
case 1:
|
||||
glTextureSubImage1D(tex_id_, mip, offset[0], extent[0], format, type, data);
|
||||
break;
|
||||
case 2:
|
||||
glTextureSubImage2D(tex_id_, mip, UNPACK2(offset), UNPACK2(extent), format, type, data);
|
||||
break;
|
||||
case 3:
|
||||
glTextureSubImage3D(tex_id_, mip, UNPACK3(offset), UNPACK3(extent), format, type, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
has_pixels_ = true;
|
||||
}
|
||||
|
||||
void GLTexture::update_sub(int mip,
|
||||
int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat type,
|
||||
const void *data,
|
||||
const uint unpack_row_length)
|
||||
{
|
||||
BLI_assert(validate_data_format(format_, type));
|
||||
BLI_assert(data != nullptr);
|
||||
|
||||
if (mip >= mipmaps_) {
|
||||
debug::raise_gl_error("Updating a miplvl on a texture too small to have this many levels.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* If `unpack_row_length` is 0, rows are sequentially stored. Otherwise we unpack data
|
||||
* into a staging block, so the half conversion below doesn't happen on the full input. */
|
||||
const bool do_texture_unpack = !ELEM(unpack_row_length, 0, extent[0]);
|
||||
|
||||
/* Unpack `data` if `unpack_row_length` is set. */
|
||||
std::unique_ptr<uint8_t, MEM_smart_ptr_deleter<uint8_t>> unpack_buffer = nullptr;
|
||||
if (do_texture_unpack) {
|
||||
BLI_assert_msg(!(format_flag_ & GPU_FORMAT_COMPRESSED),
|
||||
"Compressed data with unpack_row_length != 0 is not supported.");
|
||||
BLI_assert_msg(extent[2] <= 1,
|
||||
"3D texture data with unpack_row_length != 0 is not supported.");
|
||||
|
||||
size_t src_row_stride = unpack_row_length * to_bytesize(format_, type);
|
||||
size_t dst_row_stride = max_ii(extent[0], 1) * to_bytesize(format_, type);
|
||||
size_t dst_total_count = dst_row_stride * max_ii(extent[1], 1) * max_ii(extent[2], 1);
|
||||
|
||||
/* Allocate buffer to size necessary for gather */
|
||||
unpack_buffer.reset(
|
||||
static_cast<uint8_t *>(MEM_new_uninitialized_aligned(dst_total_count, 128, __func__)));
|
||||
|
||||
/* Strided loop; we advance source and destination pointers separately during a gather. */
|
||||
const uint8_t *src_ptr = static_cast<const uint8_t *>(data);
|
||||
uint8_t *dst_ptr = unpack_buffer.get();
|
||||
for (int y = 0; y < max_ii(extent[1], 1); ++y) {
|
||||
std::memcpy(dst_ptr, src_ptr, dst_row_stride);
|
||||
src_ptr += src_row_stride;
|
||||
dst_ptr += dst_row_stride;
|
||||
}
|
||||
|
||||
/* Replace the 'data' ptr with `unpack_buffer`,
|
||||
* which has lifetime in the function scope. */
|
||||
data = unpack_buffer.get();
|
||||
}
|
||||
|
||||
GLenum gl_type = to_gl(type);
|
||||
|
||||
/* If `data` is float and target storage is half, convert to half */
|
||||
std::unique_ptr<uint16_t, MEM_smart_ptr_deleter<uint16_t>> clamped_half_buffer = nullptr;
|
||||
if (type == GPU_DATA_FLOAT && is_half_float(format_)) {
|
||||
size_t dst_pixel_count = max_ii(extent[0], 1) * max_ii(extent[1], 1) * max_ii(extent[2], 1);
|
||||
size_t dst_total_count = to_component_len(format_) * dst_pixel_count;
|
||||
|
||||
/* Allocate buffer to size necessary for conversion.. */
|
||||
clamped_half_buffer.reset(static_cast<uint16_t *>(
|
||||
MEM_new_uninitialized_aligned(sizeof(uint16_t) * dst_total_count, 128, __func__)));
|
||||
|
||||
Span<float> src(static_cast<const float *>(data), dst_total_count);
|
||||
MutableSpan<uint16_t> dst(static_cast<uint16_t *>(clamped_half_buffer.get()), dst_total_count);
|
||||
|
||||
constexpr int64_t chunk_size = 4 * 1024 * 1024;
|
||||
threading::parallel_for(IndexRange(dst_total_count), chunk_size, [&](const IndexRange range) {
|
||||
/* Doing float to half conversion manually to avoid implementation specific behavior
|
||||
* regarding Inf and NaNs. Use make finite version to avoid unexpected black pixels on
|
||||
* certain implementation. For platform parity we clamp these infinite values to finite
|
||||
* values. */
|
||||
math::float_to_half_make_finite_array(
|
||||
src.slice(range).data(), dst.slice(range).data(), range.size());
|
||||
});
|
||||
|
||||
/* Replace the 'data' ptr with `clamped_half_buffer`,
|
||||
* which has lifetime in the function scope. */
|
||||
data = clamped_half_buffer.get();
|
||||
gl_type = to_gl(GPU_DATA_HALF_FLOAT);
|
||||
|
||||
/* If the `data` ptr was previously replaced by `unpack_buffer`,
|
||||
* clear `unpack_buffer` as it is no longer necessary. */
|
||||
if (do_texture_unpack) {
|
||||
unpack_buffer.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* TextureFormat::SINT_16_16 formats with integer data does not seem to work correctly in all GPU
|
||||
* drivers, so convert to short and use GL_SHORT. */
|
||||
std::unique_ptr<int16_t, MEM_smart_ptr_deleter<int16_t>> short_buffer = nullptr;
|
||||
if (type == GPU_DATA_INT && format_ == TextureFormat::SINT_16_16) {
|
||||
size_t dst_pixel_count = max_ii(extent[0], 1) * max_ii(extent[1], 1) * max_ii(extent[2], 1);
|
||||
size_t dst_total_count = to_component_len(format_) * dst_pixel_count;
|
||||
|
||||
short_buffer.reset(static_cast<int16_t *>(
|
||||
MEM_new_uninitialized_aligned(sizeof(int16_t) * dst_total_count, 128, __func__)));
|
||||
|
||||
Span<int32_t> src(static_cast<const int32_t *>(data), dst_total_count);
|
||||
MutableSpan<int16_t> dst(static_cast<int16_t *>(short_buffer.get()), dst_total_count);
|
||||
|
||||
constexpr int64_t chunk_size = 4 * 1024 * 1024;
|
||||
threading::parallel_for(IndexRange(dst_total_count), chunk_size, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
dst[i] = int16_t(src[i]);
|
||||
}
|
||||
});
|
||||
|
||||
/* Replace the 'data' ptr with `short_buffer`, which has lifetime in the function scope. */
|
||||
data = short_buffer.get();
|
||||
gl_type = GL_SHORT;
|
||||
|
||||
/* If the `data` ptr was previously replaced by `unpack_buffer`,
|
||||
* clear `unpack_buffer` as it is no longer necessary. */
|
||||
if (do_texture_unpack) {
|
||||
unpack_buffer.reset(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
const int dimensions = this->dimensions_count();
|
||||
GLenum gl_format = to_gl_data_format(format_);
|
||||
|
||||
/* Some drivers have issues with cubemap & glTextureSubImage3D even if it is correct. */
|
||||
if (GLContext::direct_state_access_support && (type_ != GPU_TEXTURE_CUBE)) {
|
||||
this->update_sub_direct_state_access(mip, offset, extent, gl_format, gl_type, data);
|
||||
return;
|
||||
}
|
||||
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
if (type_ == GPU_TEXTURE_CUBE) {
|
||||
for (int i = 0; i < extent[2]; i++) {
|
||||
GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + offset[2] + i;
|
||||
glTexSubImage2D(target, mip, UNPACK2(offset), UNPACK2(extent), gl_format, gl_type, data);
|
||||
}
|
||||
}
|
||||
else if (format_flag_ & GPU_FORMAT_COMPRESSED) {
|
||||
size_t size = ((extent[0] + 3) / 4) * ((extent[1] + 3) / 4) * to_block_size(format_);
|
||||
switch (dimensions) {
|
||||
default:
|
||||
case 1:
|
||||
glCompressedTexSubImage1D(target_, mip, offset[0], extent[0], gl_format, size, data);
|
||||
break;
|
||||
case 2:
|
||||
glCompressedTexSubImage2D(
|
||||
target_, mip, UNPACK2(offset), UNPACK2(extent), gl_format, size, data);
|
||||
break;
|
||||
case 3:
|
||||
glCompressedTexSubImage3D(
|
||||
target_, mip, UNPACK3(offset), UNPACK3(extent), gl_format, size, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (dimensions) {
|
||||
default:
|
||||
case 1:
|
||||
glTexSubImage1D(target_, mip, offset[0], extent[0], gl_format, gl_type, data);
|
||||
break;
|
||||
case 2:
|
||||
glTexSubImage2D(target_, mip, UNPACK2(offset), UNPACK2(extent), gl_format, gl_type, data);
|
||||
break;
|
||||
case 3:
|
||||
glTexSubImage3D(target_, mip, UNPACK3(offset), UNPACK3(extent), gl_format, gl_type, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
has_pixels_ = true;
|
||||
}
|
||||
|
||||
void GLTexture::update_sub(int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat format,
|
||||
GPUPixelBuffer *pixbuf)
|
||||
{
|
||||
/* Update texture from pixel buffer. */
|
||||
BLI_assert(validate_data_format(format_, format));
|
||||
BLI_assert(pixbuf != nullptr);
|
||||
|
||||
const int dimensions = this->dimensions_count();
|
||||
GLenum gl_format = to_gl_data_format(format_);
|
||||
GLenum gl_type = to_gl(format);
|
||||
|
||||
/* Temporarily Bind texture. */
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
|
||||
/* Bind pixel buffer for source data. */
|
||||
GLint pix_buf_handle = GLint(GPU_pixel_buffer_get_native_handle(pixbuf).handle);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pix_buf_handle);
|
||||
|
||||
switch (dimensions) {
|
||||
default:
|
||||
case 1:
|
||||
glTexSubImage1D(target_, 0, offset[0], extent[0], gl_format, gl_type, nullptr);
|
||||
break;
|
||||
case 2:
|
||||
glTexSubImage2D(target_, 0, UNPACK2(offset), UNPACK2(extent), gl_format, gl_type, nullptr);
|
||||
break;
|
||||
case 3:
|
||||
glTexSubImage3D(target_, 0, UNPACK3(offset), UNPACK3(extent), gl_format, gl_type, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
void GLTexture::generate_mipmap()
|
||||
{
|
||||
/* Allow users to provide mipmaps stored in compressed textures.
|
||||
* Skip generating mipmaps to avoid overriding the existing ones. */
|
||||
if (format_flag_ & GPU_FORMAT_COMPRESSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Some drivers have bugs when using #glGenerateMipmap with depth textures (see #56789).
|
||||
* In this case we just create a complete texture with mipmaps manually without
|
||||
* down-sampling. You must initialize the texture levels using other methods. */
|
||||
if (format_flag_ & GPU_FORMAT_DEPTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GLContext::generate_mipmap_workaround) {
|
||||
/* Broken glGenerateMipmap, don't call it and render without mipmaps.
|
||||
* If no top level pixels have been filled in, the levels will get filled by
|
||||
* other means and there is no need to disable mipmapping. */
|
||||
if (has_pixels_) {
|
||||
this->mip_range_set(0, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Down-sample from mip 0 using implementation. */
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glGenerateTextureMipmap(tex_id_);
|
||||
}
|
||||
else {
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
glGenerateMipmap(target_);
|
||||
}
|
||||
}
|
||||
|
||||
void GLTexture::clear(const double4 data)
|
||||
{
|
||||
/* Note: do not use glClearTexImage, even if it is available (via
|
||||
* extension or GL 4.4). It causes GL framebuffer binding to be
|
||||
* way slower at least on some drivers (e.g. Win10 / NV RTX 3080,
|
||||
* but also reportedly others), as if glClearTexImage causes
|
||||
* "pixel data" to exist which is then uploaded CPU -> GPU at bind
|
||||
* time. */
|
||||
|
||||
gpu::FrameBuffer *prev_fb = GPU_framebuffer_active_get();
|
||||
|
||||
FrameBuffer *fb = this->framebuffer_get();
|
||||
fb->bind(true);
|
||||
fb->clear_attachment(this->attachment_type(0), data);
|
||||
|
||||
GPU_framebuffer_bind(prev_fb);
|
||||
}
|
||||
|
||||
void GLTexture::copy_to(Texture *dst_, IndexRange mip_levels)
|
||||
{
|
||||
GLTexture *dst = static_cast<GLTexture *>(dst_);
|
||||
GLTexture *src = this;
|
||||
|
||||
BLI_assert((dst->w_ == src->w_) && (dst->h_ == src->h_) && (dst->d_ == src->d_));
|
||||
BLI_assert((src->format_ == dst->format_) ||
|
||||
(src->format_ == TextureFormat::SRGBA_8_8_8_8 &&
|
||||
dst->format_ == TextureFormat::UNORM_8_8_8_8) ||
|
||||
(src->format_ == TextureFormat::UNORM_8_8_8_8 &&
|
||||
dst->format_ == TextureFormat::SRGBA_8_8_8_8));
|
||||
BLI_assert(dst->type_ == src->type_);
|
||||
|
||||
for (int mip : mip_levels) {
|
||||
/* NOTE: mip_size_get() won't override any dimension that is equal to 0. */
|
||||
int extent[3] = {1, 1, 1};
|
||||
this->mip_size_get(mip, extent);
|
||||
glCopyImageSubData(
|
||||
src->tex_id_, target_, mip, 0, 0, 0, dst->tex_id_, target_, mip, 0, 0, 0, UNPACK3(extent));
|
||||
}
|
||||
|
||||
has_pixels_ = true;
|
||||
}
|
||||
|
||||
void GLTexture::read(int mip, eGPUDataFormat type, void *data)
|
||||
{
|
||||
BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED));
|
||||
BLI_assert(mip <= mipmaps_ || mip == 0);
|
||||
BLI_assert(validate_data_format(format_, type));
|
||||
|
||||
size_t texture_size = read_size_get(mip, type);
|
||||
|
||||
GLenum gl_format = to_gl_data_format(
|
||||
format_ == TextureFormat::SFLOAT_32_DEPTH_UINT_8 ? TextureFormat::SFLOAT_32_DEPTH : format_);
|
||||
GLenum gl_type = to_gl(type);
|
||||
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glGetTextureImage(tex_id_, mip, gl_format, gl_type, texture_size, data);
|
||||
}
|
||||
else {
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
if (type_ == GPU_TEXTURE_CUBE) {
|
||||
size_t cube_face_size = texture_size / 6;
|
||||
char *pdata = static_cast<char *>(data);
|
||||
for (int i = 0; i < 6; i++, pdata += cube_face_size) {
|
||||
glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, mip, gl_format, gl_type, pdata);
|
||||
}
|
||||
}
|
||||
else {
|
||||
glGetTexImage(target_, mip, gl_format, gl_type, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Getters & setters
|
||||
* \{ */
|
||||
|
||||
void GLTexture::swizzle_set(const char swizzle[4])
|
||||
{
|
||||
GLint gl_swizzle[4] = {GLint(swizzle_to_gl(swizzle[0])),
|
||||
GLint(swizzle_to_gl(swizzle[1])),
|
||||
GLint(swizzle_to_gl(swizzle[2])),
|
||||
GLint(swizzle_to_gl(swizzle[3]))};
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glTextureParameteriv(tex_id_, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle);
|
||||
}
|
||||
else {
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
glTexParameteriv(target_, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle);
|
||||
}
|
||||
}
|
||||
|
||||
void GLTexture::stencil_texture_mode_set(bool use_stencil)
|
||||
{
|
||||
BLI_assert(GLContext::stencil_texturing_support);
|
||||
GLint value = use_stencil ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT;
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glTextureParameteri(tex_id_, GL_DEPTH_STENCIL_TEXTURE_MODE, value);
|
||||
}
|
||||
else {
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
glTexParameteri(target_, GL_DEPTH_STENCIL_TEXTURE_MODE, value);
|
||||
}
|
||||
}
|
||||
|
||||
void GLTexture::mip_range_set(int min, int max)
|
||||
{
|
||||
BLI_assert(min <= max && min >= 0 && max <= mipmaps_);
|
||||
mip_min_ = min;
|
||||
mip_max_ = max;
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glTextureParameteri(tex_id_, GL_TEXTURE_BASE_LEVEL, min);
|
||||
glTextureParameteri(tex_id_, GL_TEXTURE_MAX_LEVEL, max);
|
||||
}
|
||||
else {
|
||||
GLContext::state_manager_active_get()->texture_bind_temp(this);
|
||||
glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, min);
|
||||
glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, max);
|
||||
}
|
||||
}
|
||||
|
||||
FrameBuffer *GLTexture::framebuffer_get()
|
||||
{
|
||||
if (framebuffer_) {
|
||||
GLFrameBuffer *gl_framebuffer = static_cast<GLFrameBuffer *>(framebuffer_);
|
||||
if (gl_framebuffer->context_get() == GLContext::get()) {
|
||||
return framebuffer_;
|
||||
}
|
||||
|
||||
/* Textures can be shared between contexts but this helper framebuffer cannot. */
|
||||
GPU_framebuffer_free(framebuffer_);
|
||||
framebuffer_ = nullptr;
|
||||
}
|
||||
BLI_assert(!(type_ & GPU_TEXTURE_1D));
|
||||
framebuffer_ = GPU_framebuffer_create(name_.c_str());
|
||||
framebuffer_->attachment_set(this->attachment_type(0), GPU_ATTACHMENT_TEXTURE(this));
|
||||
has_pixels_ = true;
|
||||
return framebuffer_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sampler objects
|
||||
* \{ */
|
||||
|
||||
/** A function that maps GPUSamplerExtendMode values to their OpenGL enum counterparts. */
|
||||
static inline GLenum to_gl(GPUSamplerExtendMode extend_mode)
|
||||
{
|
||||
switch (extend_mode) {
|
||||
case GPU_SAMPLER_EXTEND_MODE_EXTEND:
|
||||
return GL_CLAMP_TO_EDGE;
|
||||
case GPU_SAMPLER_EXTEND_MODE_REPEAT:
|
||||
return GL_REPEAT;
|
||||
case GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT:
|
||||
return GL_MIRRORED_REPEAT;
|
||||
case GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER:
|
||||
return GL_CLAMP_TO_BORDER;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return GL_CLAMP_TO_EDGE;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint GLTexture::samplers_state_cache_[GPU_SAMPLER_EXTEND_MODES_COUNT]
|
||||
[GPU_SAMPLER_EXTEND_MODES_COUNT]
|
||||
[GPU_SAMPLER_FILTERING_TYPES_COUNT] = {};
|
||||
GLuint GLTexture::custom_samplers_state_cache_[GPU_SAMPLER_CUSTOM_TYPES_COUNT] = {};
|
||||
|
||||
void GLTexture::samplers_init()
|
||||
{
|
||||
glGenSamplers(samplers_state_cache_count_, &samplers_state_cache_[0][0][0]);
|
||||
|
||||
float max_anisotropy = 1.0f;
|
||||
if (GLContext::texture_filter_anisotropic_support) {
|
||||
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
|
||||
}
|
||||
|
||||
for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) {
|
||||
const GPUSamplerExtendMode extend_yz = static_cast<GPUSamplerExtendMode>(extend_yz_i);
|
||||
const GLenum extend_t = to_gl(extend_yz);
|
||||
|
||||
for (int extend_x_i = 0; extend_x_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_x_i++) {
|
||||
const GPUSamplerExtendMode extend_x = static_cast<GPUSamplerExtendMode>(extend_x_i);
|
||||
const GLenum extend_s = to_gl(extend_x);
|
||||
|
||||
for (int filtering_i = 0; filtering_i < GPU_SAMPLER_FILTERING_TYPES_COUNT; filtering_i++) {
|
||||
const GPUSamplerFiltering filtering = GPUSamplerFiltering(filtering_i);
|
||||
|
||||
const GLenum mag_filter = (filtering & GPU_SAMPLER_FILTERING_LINEAR) ? GL_LINEAR :
|
||||
GL_NEAREST;
|
||||
const GLenum linear_min_filter = (filtering & GPU_SAMPLER_FILTERING_MIPMAP) ?
|
||||
GL_LINEAR_MIPMAP_LINEAR :
|
||||
GL_LINEAR;
|
||||
const GLenum nearest_min_filter = (filtering & GPU_SAMPLER_FILTERING_MIPMAP) ?
|
||||
GL_NEAREST_MIPMAP_LINEAR :
|
||||
GL_NEAREST;
|
||||
const GLenum min_filter = (filtering & GPU_SAMPLER_FILTERING_LINEAR) ? linear_min_filter :
|
||||
nearest_min_filter;
|
||||
|
||||
GLuint sampler = samplers_state_cache_[extend_yz_i][extend_x_i][filtering_i];
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, extend_s);
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, extend_t);
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, extend_t);
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, min_filter);
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||
|
||||
if (GLContext::texture_filter_anisotropic_support &&
|
||||
(filtering & GPU_SAMPLER_FILTERING_MIPMAP) &&
|
||||
(filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK))
|
||||
{
|
||||
glSamplerParameterf(
|
||||
sampler,
|
||||
GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
||||
min_ff(float(GPU_anisotropic_samples_get(filtering)), max_anisotropy));
|
||||
}
|
||||
|
||||
/* Other states are left to default:
|
||||
* - GL_TEXTURE_BORDER_COLOR is {0, 0, 0, 0}.
|
||||
* - GL_TEXTURE_MIN_LOD is -1000.
|
||||
* - GL_TEXTURE_MAX_LOD is 1000.
|
||||
* - GL_TEXTURE_LOD_BIAS is 0.0f.
|
||||
*/
|
||||
|
||||
const GPUSamplerState sampler_state = {filtering, extend_x, extend_yz};
|
||||
const std::string sampler_name = sampler_state.to_string();
|
||||
debug::object_label(GL_SAMPLER, sampler, sampler_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glGenSamplers(GPU_SAMPLER_CUSTOM_TYPES_COUNT, custom_samplers_state_cache_);
|
||||
|
||||
/* Compare sampler for depth textures. */
|
||||
GLuint compare_sampler = custom_samplers_state_cache_[GPU_SAMPLER_CUSTOM_COMPARE];
|
||||
glSamplerParameteri(compare_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(compare_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(compare_sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(compare_sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(compare_sampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(compare_sampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
||||
glSamplerParameteri(compare_sampler, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
||||
|
||||
debug::object_label(GL_SAMPLER, compare_sampler, "compare");
|
||||
|
||||
/* Custom sampler for icons. The icon texture is sampled within the shader using a -0.5f LOD
|
||||
* bias. */
|
||||
GLuint icon_sampler = custom_samplers_state_cache_[GPU_SAMPLER_CUSTOM_ICON];
|
||||
glSamplerParameteri(icon_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
||||
glSamplerParameteri(icon_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
debug::object_label(GL_SAMPLER, icon_sampler, "icons");
|
||||
}
|
||||
|
||||
void GLTexture::samplers_free()
|
||||
{
|
||||
glDeleteSamplers(samplers_state_cache_count_, &samplers_state_cache_[0][0][0]);
|
||||
glDeleteSamplers(GPU_SAMPLER_CUSTOM_TYPES_COUNT, custom_samplers_state_cache_);
|
||||
}
|
||||
|
||||
GLuint GLTexture::get_sampler(const GPUSamplerState &sampler_state)
|
||||
{
|
||||
/* Internal sampler states are signal values and do not correspond to actual samplers. */
|
||||
BLI_assert(sampler_state.type != GPU_SAMPLER_STATE_TYPE_INTERNAL);
|
||||
|
||||
if (sampler_state.type == GPU_SAMPLER_STATE_TYPE_CUSTOM) {
|
||||
return custom_samplers_state_cache_[sampler_state.custom_type];
|
||||
}
|
||||
|
||||
return samplers_state_cache_[sampler_state.extend_yz][sampler_state.extend_x]
|
||||
[sampler_state.filtering];
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Proxy texture
|
||||
*
|
||||
* Dummy texture to see if the implementation supports the requested size.
|
||||
* \{ */
|
||||
|
||||
bool GLTexture::proxy_check(int mip)
|
||||
{
|
||||
/* NOTE: This only checks if this mipmap is valid / supported.
|
||||
* TODO(fclem): make the check cover the whole mipmap chain. */
|
||||
|
||||
/* Manual validation first, since some implementation have issues with proxy creation. */
|
||||
int max_size = GPU_max_texture_size();
|
||||
int max_3d_size = GPU_max_texture_3d_size();
|
||||
int max_cube_size = GLContext::max_cubemap_size;
|
||||
int size[3] = {1, 1, 1};
|
||||
this->mip_size_get(mip, size);
|
||||
|
||||
if (type_ & GPU_TEXTURE_ARRAY) {
|
||||
if (this->layer_count() > GPU_max_texture_layers()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (type_ == GPU_TEXTURE_3D) {
|
||||
if (size[0] > max_3d_size || size[1] > max_3d_size || size[2] > max_3d_size) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((type_ & ~GPU_TEXTURE_ARRAY) == GPU_TEXTURE_2D) {
|
||||
if (size[0] > max_size || size[1] > max_size) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((type_ & ~GPU_TEXTURE_ARRAY) == GPU_TEXTURE_1D) {
|
||||
if (size[0] > max_size) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ((type_ & ~GPU_TEXTURE_ARRAY) == GPU_TEXTURE_CUBE) {
|
||||
if (size[0] > max_cube_size) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_WIN, GPU_DRIVER_ANY) ||
|
||||
GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL))
|
||||
{
|
||||
/* Some AMD drivers have a faulty `GL_PROXY_TEXTURE_..` check.
|
||||
* (see #55888, #56185, #59351).
|
||||
* Checking with `GL_PROXY_TEXTURE_..` doesn't prevent `Out Of Memory` issue,
|
||||
* it just states that the OGL implementation can support the texture.
|
||||
* So we already manually check the maximum size and maximum number of layers.
|
||||
* Same thing happens on Nvidia/macOS 10.15 (#78175). */
|
||||
return true;
|
||||
}
|
||||
|
||||
GLenum gl_proxy = to_gl_proxy(type_);
|
||||
GLenum internal_format = to_gl_internal_format(format_);
|
||||
GLenum gl_format = to_gl_data_format(format_);
|
||||
GLenum gl_type = to_gl(to_texture_data_format(format_));
|
||||
/* Small exception. */
|
||||
int dimensions = (type_ == GPU_TEXTURE_CUBE) ? 2 : this->dimensions_count();
|
||||
|
||||
if (format_flag_ & GPU_FORMAT_COMPRESSED) {
|
||||
size_t img_size = ((size[0] + 3) / 4) * ((size[1] + 3) / 4) * to_block_size(format_);
|
||||
switch (dimensions) {
|
||||
default:
|
||||
case 1:
|
||||
glCompressedTexImage1D(gl_proxy, mip, size[0], 0, gl_format, img_size, nullptr);
|
||||
break;
|
||||
case 2:
|
||||
glCompressedTexImage2D(gl_proxy, mip, UNPACK2(size), 0, gl_format, img_size, nullptr);
|
||||
break;
|
||||
case 3:
|
||||
glCompressedTexImage3D(gl_proxy, mip, UNPACK3(size), 0, gl_format, img_size, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (dimensions) {
|
||||
default:
|
||||
case 1:
|
||||
glTexImage1D(gl_proxy, mip, internal_format, size[0], 0, gl_format, gl_type, nullptr);
|
||||
break;
|
||||
case 2:
|
||||
glTexImage2D(
|
||||
gl_proxy, mip, internal_format, UNPACK2(size), 0, gl_format, gl_type, nullptr);
|
||||
break;
|
||||
case 3:
|
||||
glTexImage3D(
|
||||
gl_proxy, mip, internal_format, UNPACK3(size), 0, gl_format, gl_type, nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int width = 0;
|
||||
glGetTexLevelParameteriv(gl_proxy, 0, GL_TEXTURE_WIDTH, &width);
|
||||
return (width > 0);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void GLTexture::check_feedback_loop()
|
||||
{
|
||||
/* Do not check if using compute shader. */
|
||||
GLShader *sh = dynamic_cast<GLShader *>(Context::get()->shader);
|
||||
if (sh && sh->is_compute()) {
|
||||
return;
|
||||
}
|
||||
GLFrameBuffer *fb = static_cast<GLFrameBuffer *>(GLContext::get()->active_fb);
|
||||
for (int i = 0; i < ARRAY_SIZE(fb_); i++) {
|
||||
if (fb_[i] == fb) {
|
||||
GPUAttachmentType type = fb_attachment_[i];
|
||||
GPUAttachment attachment = fb->attachments_[type];
|
||||
/* Check for when texture is used with texture barrier. */
|
||||
GPUAttachment attachment_read = fb->tmp_detached_[type];
|
||||
if (attachment.mip <= mip_max_ && attachment.mip >= mip_min_ &&
|
||||
attachment_read.tex == nullptr)
|
||||
{
|
||||
char msg[256];
|
||||
SNPRINTF(msg,
|
||||
"Feedback loop: Trying to bind a texture (%s) with mip range %d-%d but mip %d is "
|
||||
"attached to the active framebuffer (%s)",
|
||||
name_.c_str(),
|
||||
mip_min_,
|
||||
mip_max_,
|
||||
attachment.mip,
|
||||
fb->name_);
|
||||
debug::raise_gl_error(msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Pixel Buffer
|
||||
* \{ */
|
||||
|
||||
GLPixelBuffer::GLPixelBuffer(size_t size) : PixelBuffer(size)
|
||||
{
|
||||
glGenBuffers(1, &gl_id_);
|
||||
BLI_assert(gl_id_);
|
||||
|
||||
if (!gl_id_) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure size is non-zero for pixel buffer backing storage creation. */
|
||||
size = max_ii(size, 32);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_id_);
|
||||
glBufferData(GL_PIXEL_UNPACK_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
GLPixelBuffer::~GLPixelBuffer()
|
||||
{
|
||||
if (!gl_id_) {
|
||||
return;
|
||||
}
|
||||
glDeleteBuffers(1, &gl_id_);
|
||||
}
|
||||
|
||||
void *GLPixelBuffer::map()
|
||||
{
|
||||
if (!gl_id_) {
|
||||
BLI_assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_id_);
|
||||
void *ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
||||
BLI_assert(ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void GLPixelBuffer::unmap()
|
||||
{
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
GPUPixelBufferNativeHandle GLPixelBuffer::get_native_handle()
|
||||
{
|
||||
GPUPixelBufferNativeHandle native_handle;
|
||||
native_handle.handle = int64_t(gl_id_);
|
||||
native_handle.size = size_;
|
||||
return native_handle;
|
||||
}
|
||||
|
||||
size_t GLPixelBuffer::get_size()
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
} // namespace blender::gpu
|
||||
386
blender-5.2.0/source/blender/gpu/opengl/gl_texture.hh
Normal file
386
blender-5.2.0/source/blender/gpu/opengl/gl_texture.hh
Normal file
@@ -0,0 +1,386 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
#include "gpu_texture_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLTexturePool;
|
||||
|
||||
class GLTexture : public Texture {
|
||||
friend class GLStateManager;
|
||||
friend class GLFrameBuffer;
|
||||
friend class GLTexturePool;
|
||||
|
||||
private:
|
||||
/**
|
||||
* A cache of all possible sampler configurations stored along each of the three axis of
|
||||
* variation. The first and second variation axis are the wrap mode along x and y axis
|
||||
* respectively, and the third variation axis is the filtering type. See the samplers_init()
|
||||
* method for more information.
|
||||
*/
|
||||
static GLuint samplers_state_cache_[GPU_SAMPLER_EXTEND_MODES_COUNT]
|
||||
[GPU_SAMPLER_EXTEND_MODES_COUNT]
|
||||
[GPU_SAMPLER_FILTERING_TYPES_COUNT];
|
||||
static const int samplers_state_cache_count_ = GPU_SAMPLER_EXTEND_MODES_COUNT *
|
||||
GPU_SAMPLER_EXTEND_MODES_COUNT *
|
||||
GPU_SAMPLER_FILTERING_TYPES_COUNT;
|
||||
/**
|
||||
* A cache of all custom sampler configurations described in GPUSamplerCustomType. See the
|
||||
* samplers_init() method for more information.
|
||||
*/
|
||||
static GLuint custom_samplers_state_cache_[GPU_SAMPLER_CUSTOM_TYPES_COUNT];
|
||||
|
||||
/** Target to bind the texture to (#GL_TEXTURE_1D, #GL_TEXTURE_2D, etc...). */
|
||||
GLenum target_ = -1;
|
||||
/** opengl identifier for texture. */
|
||||
GLuint tex_id_ = 0;
|
||||
/** Legacy workaround for texture copy. Created when using framebuffer_get(). */
|
||||
FrameBuffer *framebuffer_ = nullptr;
|
||||
/** True if this texture is bound to at least one texture unit. */
|
||||
/* TODO(fclem): How do we ensure thread safety here? */
|
||||
bool is_bound_ = false;
|
||||
/** Same as is_bound_ but for image slots. */
|
||||
bool is_bound_image_ = false;
|
||||
/** True if pixels in the texture have been initialized. */
|
||||
bool has_pixels_ = false;
|
||||
|
||||
public:
|
||||
GLTexture(const char *name);
|
||||
~GLTexture();
|
||||
|
||||
void update_sub(int mip,
|
||||
int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat type,
|
||||
const void *data,
|
||||
const uint unpack_row_length = 0) override;
|
||||
void update_sub(int offset[3],
|
||||
int extent[3],
|
||||
eGPUDataFormat format,
|
||||
GPUPixelBuffer *pixbuf) override;
|
||||
|
||||
/**
|
||||
* This will create the mipmap images and populate them with filtered data from base level.
|
||||
*
|
||||
* \warning Depth textures are not populated but they have their mips correctly defined.
|
||||
* \warning This resets the mipmap range.
|
||||
*/
|
||||
void generate_mipmap() override;
|
||||
void copy_to(Texture *dst, IndexRange mip_levels) override;
|
||||
void clear(const double4 data) override;
|
||||
void swizzle_set(const char swizzle_mask[4]) override;
|
||||
void mip_range_set(int min, int max) override;
|
||||
void read(int mip, eGPUDataFormat type, void *data) override;
|
||||
|
||||
void check_feedback_loop();
|
||||
|
||||
/**
|
||||
* Pre-generate, setup all possible samplers and cache them in the samplers_state_cache_ and
|
||||
* custom_samplers_state_cache_ arrays. This is done to avoid the runtime cost associated with
|
||||
* setting up a sampler at draw time.
|
||||
*/
|
||||
static void samplers_init();
|
||||
|
||||
/**
|
||||
* Free the samplers cache generated in samplers_init() method.
|
||||
*/
|
||||
static void samplers_free();
|
||||
|
||||
/**
|
||||
* Get the handle of the OpenGL sampler that corresponds to the given sampler state.
|
||||
* The sampler is retrieved from the cached samplers computed in the samplers_init() method.
|
||||
*/
|
||||
static GLuint get_sampler(const GPUSamplerState &sampler_state);
|
||||
|
||||
protected:
|
||||
/** Return true on success. */
|
||||
bool init_internal() override;
|
||||
/** Return true on success. */
|
||||
bool init_internal(VertBuf *vbo) override;
|
||||
/** Return true on success. */
|
||||
bool init_internal(gpu::Texture *src,
|
||||
int mip_offset,
|
||||
int layer_offset,
|
||||
bool use_stencil) override;
|
||||
|
||||
private:
|
||||
bool proxy_check(int mip);
|
||||
void stencil_texture_mode_set(bool use_stencil);
|
||||
void update_sub_direct_state_access(
|
||||
int mip, int offset[3], int extent[3], GLenum gl_format, GLenum gl_type, const void *data);
|
||||
FrameBuffer *framebuffer_get();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLTexture")
|
||||
};
|
||||
|
||||
class GLPixelBuffer : public PixelBuffer {
|
||||
private:
|
||||
GLuint gl_id_ = 0;
|
||||
|
||||
public:
|
||||
GLPixelBuffer(size_t size);
|
||||
~GLPixelBuffer();
|
||||
|
||||
void *map() override;
|
||||
void unmap() override;
|
||||
GPUPixelBufferNativeHandle get_native_handle() override;
|
||||
size_t get_size() override;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLPixelBuffer")
|
||||
};
|
||||
|
||||
inline GLenum to_gl_internal_format(TextureFormat format)
|
||||
{
|
||||
#define CASE(a, b, c, blender_enum, d, e, f, gl_pixel_enum, h) \
|
||||
case TextureFormat::blender_enum: \
|
||||
return GL_##gl_pixel_enum;
|
||||
|
||||
switch (format) {
|
||||
GPU_TEXTURE_FORMAT_EXPAND(CASE)
|
||||
case TextureFormat::Invalid:
|
||||
break;
|
||||
}
|
||||
#undef CASE
|
||||
BLI_assert_msg(0, "Texture format incorrect or unsupported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline GLenum to_gl_target(GPUTextureType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_TEXTURE_1D:
|
||||
return GL_TEXTURE_1D;
|
||||
case GPU_TEXTURE_1D_ARRAY:
|
||||
return GL_TEXTURE_1D_ARRAY;
|
||||
case GPU_TEXTURE_2D:
|
||||
return GL_TEXTURE_2D;
|
||||
case GPU_TEXTURE_2D_ARRAY:
|
||||
return GL_TEXTURE_2D_ARRAY;
|
||||
case GPU_TEXTURE_3D:
|
||||
return GL_TEXTURE_3D;
|
||||
case GPU_TEXTURE_CUBE:
|
||||
return GL_TEXTURE_CUBE_MAP;
|
||||
case GPU_TEXTURE_CUBE_ARRAY:
|
||||
return GL_TEXTURE_CUBE_MAP_ARRAY_ARB;
|
||||
case GPU_TEXTURE_BUFFER:
|
||||
return GL_TEXTURE_BUFFER;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return GL_TEXTURE_1D;
|
||||
}
|
||||
}
|
||||
|
||||
inline GLenum to_gl_proxy(GPUTextureType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_TEXTURE_1D:
|
||||
return GL_PROXY_TEXTURE_1D;
|
||||
case GPU_TEXTURE_1D_ARRAY:
|
||||
return GL_PROXY_TEXTURE_1D_ARRAY;
|
||||
case GPU_TEXTURE_2D:
|
||||
return GL_PROXY_TEXTURE_2D;
|
||||
case GPU_TEXTURE_2D_ARRAY:
|
||||
return GL_PROXY_TEXTURE_2D_ARRAY;
|
||||
case GPU_TEXTURE_3D:
|
||||
return GL_PROXY_TEXTURE_3D;
|
||||
case GPU_TEXTURE_CUBE:
|
||||
return GL_PROXY_TEXTURE_CUBE_MAP;
|
||||
case GPU_TEXTURE_CUBE_ARRAY:
|
||||
return GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB;
|
||||
case GPU_TEXTURE_BUFFER:
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return GL_TEXTURE_1D;
|
||||
}
|
||||
}
|
||||
|
||||
inline GLenum swizzle_to_gl(const char swizzle)
|
||||
{
|
||||
switch (swizzle) {
|
||||
default:
|
||||
case 'x':
|
||||
case 'r':
|
||||
return GL_RED;
|
||||
case 'y':
|
||||
case 'g':
|
||||
return GL_GREEN;
|
||||
case 'z':
|
||||
case 'b':
|
||||
return GL_BLUE;
|
||||
case 'w':
|
||||
case 'a':
|
||||
return GL_ALPHA;
|
||||
case '0':
|
||||
return GL_ZERO;
|
||||
case '1':
|
||||
return GL_ONE;
|
||||
}
|
||||
}
|
||||
|
||||
inline GLenum to_gl(eGPUDataFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case GPU_DATA_FLOAT:
|
||||
return GL_FLOAT;
|
||||
case GPU_DATA_INT:
|
||||
return GL_INT;
|
||||
case GPU_DATA_UINT:
|
||||
return GL_UNSIGNED_INT;
|
||||
case GPU_DATA_UBYTE:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case GPU_DATA_UINT_24_8_DEPRECATED:
|
||||
return GL_UNSIGNED_INT_24_8;
|
||||
case GPU_DATA_2_10_10_10_REV:
|
||||
return GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return GL_UNSIGNED_INT_10F_11F_11F_REV;
|
||||
case GPU_DATA_HALF_FLOAT:
|
||||
return GL_HALF_FLOAT;
|
||||
default:
|
||||
BLI_assert_msg(0, "Unhandled data format");
|
||||
return GL_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
inline GLenum to_gl_data_format(TextureFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
/* Texture & Render-Buffer Formats. */
|
||||
case TextureFormat::UNORM_8_8_8_8:
|
||||
case TextureFormat::SFLOAT_32_32_32_32:
|
||||
case TextureFormat::SFLOAT_16_16_16_16:
|
||||
case TextureFormat::UNORM_16_16_16_16:
|
||||
return GL_RGBA;
|
||||
case TextureFormat::UINT_8_8_8_8:
|
||||
case TextureFormat::SINT_8_8_8_8:
|
||||
case TextureFormat::SINT_32_32_32_32:
|
||||
case TextureFormat::UINT_32_32_32_32:
|
||||
case TextureFormat::UINT_16_16_16_16:
|
||||
case TextureFormat::SINT_16_16_16_16:
|
||||
return GL_RGBA_INTEGER;
|
||||
case TextureFormat::UNORM_8_8:
|
||||
case TextureFormat::SFLOAT_32_32:
|
||||
case TextureFormat::SFLOAT_16_16:
|
||||
case TextureFormat::UNORM_16_16:
|
||||
return GL_RG;
|
||||
case TextureFormat::UINT_8_8:
|
||||
case TextureFormat::SINT_8_8:
|
||||
case TextureFormat::UINT_32_32:
|
||||
case TextureFormat::SINT_32_32:
|
||||
case TextureFormat::SINT_16_16:
|
||||
case TextureFormat::UINT_16_16:
|
||||
return GL_RG_INTEGER;
|
||||
case TextureFormat::UNORM_8:
|
||||
case TextureFormat::SFLOAT_32:
|
||||
case TextureFormat::SFLOAT_16:
|
||||
case TextureFormat::UNORM_16:
|
||||
return GL_RED;
|
||||
case TextureFormat::UINT_8:
|
||||
case TextureFormat::SINT_8:
|
||||
case TextureFormat::UINT_32:
|
||||
case TextureFormat::SINT_32:
|
||||
case TextureFormat::UINT_16:
|
||||
case TextureFormat::SINT_16:
|
||||
return GL_RED_INTEGER;
|
||||
/* Special formats texture & render-buffer. */
|
||||
case TextureFormat::UINT_10_10_10_2:
|
||||
case TextureFormat::UNORM_10_10_10_2:
|
||||
case TextureFormat::SRGBA_8_8_8_8:
|
||||
return GL_RGBA;
|
||||
case TextureFormat::UFLOAT_11_11_10:
|
||||
return GL_RGB;
|
||||
case TextureFormat::SFLOAT_32_DEPTH_UINT_8:
|
||||
return GL_DEPTH_STENCIL;
|
||||
/* Texture only formats. */
|
||||
case TextureFormat::SNORM_16_16_16_16:
|
||||
case TextureFormat::SNORM_8_8_8_8:
|
||||
return GL_RGBA;
|
||||
case TextureFormat::SFLOAT_16_16_16:
|
||||
case TextureFormat::SFLOAT_32_32_32:
|
||||
case TextureFormat::SINT_32_32_32:
|
||||
case TextureFormat::UINT_32_32_32:
|
||||
case TextureFormat::SNORM_16_16_16:
|
||||
case TextureFormat::SINT_16_16_16:
|
||||
case TextureFormat::UINT_16_16_16:
|
||||
case TextureFormat::UNORM_16_16_16:
|
||||
case TextureFormat::SNORM_8_8_8:
|
||||
case TextureFormat::UNORM_8_8_8:
|
||||
case TextureFormat::SINT_8_8_8:
|
||||
case TextureFormat::UINT_8_8_8:
|
||||
return GL_RGB;
|
||||
case TextureFormat::SNORM_16_16:
|
||||
case TextureFormat::SNORM_8_8:
|
||||
return GL_RG;
|
||||
case TextureFormat::SNORM_16:
|
||||
case TextureFormat::SNORM_8:
|
||||
return GL_RED;
|
||||
/* Special formats, texture only. */
|
||||
case TextureFormat::SRGB_DXT1:
|
||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
|
||||
case TextureFormat::SRGB_DXT3:
|
||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
|
||||
case TextureFormat::SRGB_DXT5:
|
||||
return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
|
||||
case TextureFormat::SNORM_DXT1:
|
||||
return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
case TextureFormat::SNORM_DXT3:
|
||||
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
case TextureFormat::SNORM_DXT5:
|
||||
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
case TextureFormat::SRGBA_8_8_8:
|
||||
case TextureFormat::UFLOAT_9_9_9_EXP_5:
|
||||
return GL_RGB;
|
||||
/* Depth Formats. */
|
||||
case TextureFormat::SFLOAT_32_DEPTH:
|
||||
case TextureFormat::UNORM_16_DEPTH:
|
||||
return GL_DEPTH_COMPONENT;
|
||||
case TextureFormat::Invalid:
|
||||
break;
|
||||
}
|
||||
BLI_assert_msg(0, "Texture format incorrect or unsupported\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assume UNORM/Float target. Used with #glReadPixels.
|
||||
*/
|
||||
inline GLenum channel_len_to_gl(int channel_len)
|
||||
{
|
||||
switch (channel_len) {
|
||||
case 1:
|
||||
return GL_RED;
|
||||
case 2:
|
||||
return GL_RG;
|
||||
case 3:
|
||||
return GL_RGB;
|
||||
case 4:
|
||||
return GL_RGBA;
|
||||
default:
|
||||
BLI_assert_msg(0, "Wrong number of texture channels");
|
||||
return GL_RED;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE GLTexture *unwrap(Texture *tex)
|
||||
{
|
||||
return static_cast<GLTexture *>(tex);
|
||||
}
|
||||
|
||||
BLI_INLINE Texture *wrap(GLTexture *texture)
|
||||
{
|
||||
return static_cast<Texture *>(texture);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
289
blender-5.2.0/source/blender/gpu/opengl/gl_texture_pool.cc
Normal file
289
blender-5.2.0/source/blender/gpu/opengl/gl_texture_pool.cc
Normal file
@@ -0,0 +1,289 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "gl_backend.hh"
|
||||
#include "gl_texture_pool.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
static CLG_LogRef LOG = {"gpu.opengl"};
|
||||
|
||||
/* Given a TextureFormat, return an underlying format on which to alias. If the
|
||||
* format does not support aliasing to another format, simply return the input. */
|
||||
static TextureFormat get_compatible_texture_format(TextureFormat format)
|
||||
{
|
||||
/* glTextureView doesn't support aliasing on depth, stencil, or most compressed formats. */
|
||||
GPUTextureFormatFlag format_flag = to_format_flag(format);
|
||||
if (bool(format_flag & GPU_FORMAT_DEPTH_STENCIL)) {
|
||||
return format;
|
||||
}
|
||||
if (bool(format_flag & GPU_FORMAT_COMPRESSED)) {
|
||||
return format;
|
||||
}
|
||||
|
||||
/* Given expected byte size, we use a default format available as write/target format. */
|
||||
switch (to_bytesize(format)) {
|
||||
case 16:
|
||||
return TextureFormat::SFLOAT_32_32_32_32;
|
||||
case 8:
|
||||
return TextureFormat::SFLOAT_32_32;
|
||||
case 4:
|
||||
return TextureFormat::SFLOAT_32;
|
||||
case 2:
|
||||
return TextureFormat::SFLOAT_16;
|
||||
case 1:
|
||||
return TextureFormat::UINT_8;
|
||||
default:
|
||||
return TextureFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
GLTexturePool::~GLTexturePool()
|
||||
{
|
||||
for (const TextureHandle &handle : acquired_) {
|
||||
release_texture(wrap(handle.view));
|
||||
}
|
||||
for (AllocationHandle &handle : pool_) {
|
||||
GPU_texture_free(handle.texture);
|
||||
}
|
||||
}
|
||||
|
||||
Texture *GLTexturePool::acquire_texture_impl(int3 extent,
|
||||
int mip_len,
|
||||
GPUTextureType type,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage,
|
||||
const char *name)
|
||||
{
|
||||
/* Determine format of compatible underlying texture. If there is no
|
||||
* compatible format to alias upon, we simply require an exact match
|
||||
* for the underlying texture. */
|
||||
TextureFormat compatible_format = get_compatible_texture_format(format);
|
||||
BLI_assert(compatible_format != TextureFormat::Invalid);
|
||||
|
||||
/* Determine actual mipmap depth. */
|
||||
int mip_len_max = 1 + floorf(log2f(std::max({extent.x, extent.y, extent.z})));
|
||||
mip_len = min_ii(mip_len, mip_len_max);
|
||||
|
||||
/* Search for the first compatible existing texture. */
|
||||
int64_t match_index = -1;
|
||||
for (uint64_t i : pool_.index_range()) {
|
||||
const AllocationHandle &handle = pool_[i];
|
||||
if (handle.texture->format_ != compatible_format) {
|
||||
continue;
|
||||
}
|
||||
if (int3(handle.texture->w_, handle.texture->h_, handle.texture->d_) != extent) {
|
||||
/* TODO(not_mark): sub-view on `texture->d_`. */
|
||||
continue;
|
||||
}
|
||||
if (handle.texture->mip_count() != mip_len) {
|
||||
/* TODO(not_mark): sub-view on mip levels. */
|
||||
continue;
|
||||
}
|
||||
match_index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Return value. */
|
||||
TextureHandle texture_handle;
|
||||
|
||||
/* Acquire the compatible texture, or create a new one as a last resort. */
|
||||
if (match_index != -1) {
|
||||
texture_handle.texture = pool_[match_index].texture;
|
||||
pool_.remove_and_reorder(match_index);
|
||||
/* Override the usage. It doesn't really apply to OpenGL in practice, but `GPU_texture_usage`
|
||||
* callers may still rely on it. */
|
||||
texture_handle.texture->usage_set(usage | GPU_TEXTURE_USAGE_FORMAT_VIEW);
|
||||
}
|
||||
else {
|
||||
/* Debug label attached to allocated texture object. */
|
||||
std::string texture_name_str;
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
texture_name_str = fmt::format("TexFromPool_{}", pool_.size());
|
||||
}
|
||||
|
||||
Texture *texture = GPUBackend::get()->texture_alloc(texture_name_str.c_str());
|
||||
texture->usage_set(usage | GPU_TEXTURE_USAGE_FORMAT_VIEW);
|
||||
bool texture_result = false;
|
||||
UNUSED_VARS_NDEBUG(texture_result);
|
||||
switch (type) {
|
||||
case GPU_TEXTURE_1D:
|
||||
case GPU_TEXTURE_1D_ARRAY:
|
||||
texture_result = texture->init_1D(extent.x, extent.y, mip_len, compatible_format);
|
||||
break;
|
||||
case GPU_TEXTURE_2D:
|
||||
case GPU_TEXTURE_2D_ARRAY:
|
||||
texture_result = texture->init_2D(
|
||||
extent.x, extent.y, extent.z, mip_len, compatible_format);
|
||||
break;
|
||||
case GPU_TEXTURE_3D:
|
||||
texture_result = texture->init_3D(
|
||||
extent.x, extent.y, extent.z, mip_len, compatible_format);
|
||||
break;
|
||||
case GPU_TEXTURE_CUBE:
|
||||
case GPU_TEXTURE_CUBE_ARRAY:
|
||||
texture_result = texture->init_cubemap(extent.x, extent.y, mip_len, compatible_format);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
BLI_assert(texture_result);
|
||||
|
||||
texture_handle.texture = unwrap(texture);
|
||||
}
|
||||
|
||||
/* On acquire, issue barriers; backing texture or view may still be in flight somewhere. */
|
||||
GPUBarrier barrier = {};
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_READ) {
|
||||
barrier |= (GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH);
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_SHADER_WRITE) {
|
||||
barrier |= GPU_BARRIER_SHADER_IMAGE_ACCESS;
|
||||
}
|
||||
if (usage & GPU_TEXTURE_USAGE_ATTACHMENT) {
|
||||
barrier |= GPU_BARRIER_FRAMEBUFFER;
|
||||
}
|
||||
GPU_memory_barrier(barrier);
|
||||
|
||||
/* Debug label attached to view texture object. */
|
||||
std::string view_name_str;
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
view_name_str = name ? name : texture_handle.texture->name_;
|
||||
}
|
||||
|
||||
/* Assemble texture view and add to handle. Note, glTextureView with identical formats is
|
||||
* allowed, even if the formats are not listed for aliasing in the Internal Formats table. */
|
||||
Texture *view = GPUBackend::get()->texture_alloc(view_name_str.c_str());
|
||||
bool view_result = false;
|
||||
UNUSED_VARS_NDEBUG(view_result);
|
||||
switch (type) {
|
||||
case GPU_TEXTURE_1D:
|
||||
case GPU_TEXTURE_2D:
|
||||
case GPU_TEXTURE_3D:
|
||||
case GPU_TEXTURE_CUBE:
|
||||
view_result = view->init_view(
|
||||
texture_handle.texture, format, type, 0, mip_len, 0, 1, false, false);
|
||||
break;
|
||||
case GPU_TEXTURE_1D_ARRAY:
|
||||
view_result = view->init_view(
|
||||
texture_handle.texture, format, type, 0, mip_len, 0, extent.y, false, false);
|
||||
break;
|
||||
case GPU_TEXTURE_2D_ARRAY:
|
||||
case GPU_TEXTURE_CUBE_ARRAY:
|
||||
view_result = view->init_view(
|
||||
texture_handle.texture, format, type, 0, mip_len, 0, extent.z, false, false);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
BLI_assert(view_result);
|
||||
|
||||
/* On integer textures, disable filtering by default, as this is not guaranteed to be
|
||||
* consistently supported across backends. */
|
||||
if (GPU_texture_has_integer_format(view)) {
|
||||
view->sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_LINEAR, false);
|
||||
view->sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_MIPMAP, false);
|
||||
view->sampler_state.set_filtering_flag_from_test(GPU_SAMPLER_FILTERING_ANISOTROPIC_MASK,
|
||||
false);
|
||||
}
|
||||
|
||||
texture_handle.view = unwrap(view);
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
current_usage_data_.usage_count++;
|
||||
current_usage_data_.usage_count_max = std::max(current_usage_data_.usage_count,
|
||||
current_usage_data_.usage_count_max);
|
||||
}
|
||||
|
||||
acquired_.add(texture_handle);
|
||||
return wrap(texture_handle.view);
|
||||
}
|
||||
|
||||
void GLTexturePool::release_texture(Texture *tex)
|
||||
{
|
||||
BLI_assert_msg(acquired_.contains({unwrap(tex)}),
|
||||
"Unacquired texture passed to TexturePool::release_texture()");
|
||||
TextureHandle texture_handle = acquired_.lookup_key({unwrap(tex), {}, 1});
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
current_usage_data_.usage_count--;
|
||||
}
|
||||
|
||||
/* Move allocation back to `pool_`. */
|
||||
AllocationHandle allocation_handle;
|
||||
allocation_handle.texture = texture_handle.texture;
|
||||
pool_.append(allocation_handle);
|
||||
|
||||
/* Destroy view and handle, if a view was created. */
|
||||
GPU_texture_free(texture_handle.view);
|
||||
acquired_.remove(texture_handle);
|
||||
}
|
||||
|
||||
void GLTexturePool::offset_users_count(Texture *tex, int offset)
|
||||
{
|
||||
BLI_assert_msg(acquired_.contains({unwrap(tex)}),
|
||||
"Unacquired texture passed to TexturePool::offset_users_count()");
|
||||
TextureHandle texture_handle = acquired_.lookup_key({unwrap(tex), {}, 1});
|
||||
texture_handle.users_count += offset;
|
||||
acquired_.add_overwrite(texture_handle);
|
||||
}
|
||||
|
||||
void GLTexturePool::reset(bool force_free)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
/* Iterate acquired textures, and ensure the internal counter equals 0; otherwise
|
||||
* this indicates a missing `::retain()` or `::release()`. */
|
||||
for (const TextureHandle &tex : acquired_) {
|
||||
BLI_assert_msg(tex.users_count == 0,
|
||||
"Missing texture release/retain. Likely TextureFromPool::release(), "
|
||||
"TextureFromPool::retain() or TexturePool::release_texture().");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Reverse iterate unused allocations, to make sure we only reorder known good handles. */
|
||||
for (int i = pool_.size() - 1; i >= 0; i--) {
|
||||
AllocationHandle &handle = pool_[i];
|
||||
if (handle.unused_cycles_count >= max_unused_cycles_ || force_free) {
|
||||
GPU_texture_free(handle.texture);
|
||||
pool_.remove_and_reorder(i);
|
||||
}
|
||||
else {
|
||||
handle.unused_cycles_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (G.debug & G_DEBUG_GPU) {
|
||||
/* Log debug usage if it differs from the last reset. */
|
||||
if (!(previous_usage_data_ == current_usage_data_)) {
|
||||
log_usage_data();
|
||||
}
|
||||
|
||||
/* Reset usage data to track it for the next reset. */
|
||||
previous_usage_data_ = current_usage_data_;
|
||||
current_usage_data_ = {};
|
||||
current_usage_data_.usage_count = acquired_.size();
|
||||
}
|
||||
}
|
||||
|
||||
void GLTexturePool::log_usage_data() const
|
||||
{
|
||||
int64_t total_texture_count = acquired_.size() + pool_.size();
|
||||
CLOG_TRACE(&LOG,
|
||||
"GLTexturePool uses %ld textures (%ld consecutively)",
|
||||
static_cast<long>(total_texture_count),
|
||||
static_cast<long>(current_usage_data_.usage_count_max));
|
||||
}
|
||||
} // namespace blender::gpu
|
||||
85
blender-5.2.0/source/blender/gpu/opengl/gl_texture_pool.hh
Normal file
85
blender-5.2.0/source/blender/gpu/opengl/gl_texture_pool.hh
Normal file
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gl_texture.hh"
|
||||
#include "gpu_texture_pool_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLTexturePool : public TexturePoolBase {
|
||||
/* Defer deallocation enough cycles to avoid interleaved calls to different viewport render
|
||||
* functions (selection / display) causing constant allocation / deallocation (See #113024). */
|
||||
static constexpr int max_unused_cycles_ = 8;
|
||||
|
||||
struct AllocationHandle {
|
||||
GLTexture *texture = nullptr;
|
||||
|
||||
/* Counter to track the number of unused cycles before deallocation in `pool_`. */
|
||||
int unused_cycles_count = 0;
|
||||
};
|
||||
|
||||
struct TextureHandle {
|
||||
GLTexture *view = nullptr;
|
||||
GLTexture *texture = nullptr;
|
||||
|
||||
/* Counter to track texture acquire/retain mismatches in `acquire_`. */
|
||||
int users_count = 1;
|
||||
|
||||
/* We use the pointer as hash/comparator, as a texture cannot be acquired twice. */
|
||||
uint64_t hash() const
|
||||
{
|
||||
return get_default_hash(view);
|
||||
}
|
||||
|
||||
bool operator==(const TextureHandle &o) const
|
||||
{
|
||||
return view == o.view;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<AllocationHandle> pool_;
|
||||
Set<TextureHandle> acquired_;
|
||||
|
||||
/* Debug storage to log memory usage. Log is only output
|
||||
* if values have changed since the last `::reset()`. */
|
||||
struct LogUsageData {
|
||||
int64_t usage_count = 0;
|
||||
int64_t usage_count_max = 0;
|
||||
|
||||
bool operator==(const LogUsageData &o) const
|
||||
{
|
||||
return std::tie(usage_count, usage_count_max) == std::tie(o.usage_count, o.usage_count_max);
|
||||
}
|
||||
};
|
||||
LogUsageData previous_usage_data_ = {};
|
||||
LogUsageData current_usage_data_ = {};
|
||||
|
||||
/* Output usage data to debug log. Called on `--debug-gpu` */
|
||||
void log_usage_data() const;
|
||||
|
||||
protected:
|
||||
Texture *acquire_texture_impl(int3 extent,
|
||||
int mip_len,
|
||||
GPUTextureType type,
|
||||
TextureFormat format,
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_GENERAL,
|
||||
const char *name = nullptr) override;
|
||||
|
||||
public:
|
||||
~GLTexturePool();
|
||||
|
||||
void release_texture(Texture *tex) override;
|
||||
|
||||
void reset(bool force_free = false) override;
|
||||
|
||||
void offset_users_count(Texture *tex, int offset) override;
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
158
blender-5.2.0/source/blender/gpu/opengl/gl_uniform_buffer.cc
Normal file
158
blender-5.2.0/source/blender/gpu/opengl/gl_uniform_buffer.cc
Normal file
@@ -0,0 +1,158 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "gpu_context_private.hh"
|
||||
|
||||
#include "gl_debug.hh"
|
||||
#include "gl_texture.hh"
|
||||
#include "gl_uniform_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation & Deletion
|
||||
* \{ */
|
||||
|
||||
GLUniformBuf::GLUniformBuf(size_t size, const char *name) : UniformBuf(size, name)
|
||||
{
|
||||
/* Do not create ubo GL buffer here to allow allocation from any thread. */
|
||||
BLI_assert(size <= GPU_max_uniform_buffer_size());
|
||||
}
|
||||
|
||||
GLUniformBuf::~GLUniformBuf()
|
||||
{
|
||||
GLContext::buffer_free(ubo_id_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Data upload / update
|
||||
* \{ */
|
||||
|
||||
void GLUniformBuf::init()
|
||||
{
|
||||
BLI_assert(GLContext::get());
|
||||
|
||||
glGenBuffers(1, &ubo_id_);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_);
|
||||
glBufferData(GL_UNIFORM_BUFFER, size_in_bytes_, nullptr, GL_DYNAMIC_DRAW);
|
||||
|
||||
debug::object_label(GL_UNIFORM_BUFFER, ubo_id_, name_);
|
||||
}
|
||||
|
||||
void GLUniformBuf::update(const void *data)
|
||||
{
|
||||
if (ubo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_);
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, size_in_bytes_, data);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
|
||||
void GLUniformBuf::clear_to_zero()
|
||||
{
|
||||
if (ubo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
uint32_t data = 0;
|
||||
TextureFormat internal_format = TextureFormat::UINT_32;
|
||||
eGPUDataFormat data_format = GPU_DATA_UINT;
|
||||
|
||||
if (GLContext::direct_state_access_support) {
|
||||
glClearNamedBufferData(ubo_id_,
|
||||
to_gl_internal_format(internal_format),
|
||||
to_gl_data_format(internal_format),
|
||||
to_gl(data_format),
|
||||
&data);
|
||||
}
|
||||
else {
|
||||
/* WATCH(@fclem): This should be ok since we only use clear outside of drawing functions. */
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_);
|
||||
glClearBufferData(GL_UNIFORM_BUFFER,
|
||||
to_gl_internal_format(internal_format),
|
||||
to_gl_data_format(internal_format),
|
||||
to_gl(data_format),
|
||||
&data);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Usage
|
||||
* \{ */
|
||||
|
||||
void GLUniformBuf::bind(int slot)
|
||||
{
|
||||
if (slot >= GLContext::max_ubo_binds) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.\n",
|
||||
name_,
|
||||
slot,
|
||||
GLContext::max_ubo_binds);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ubo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
|
||||
if (data_ != nullptr) {
|
||||
this->update(data_);
|
||||
MEM_SAFE_DELETE_VOID(data_);
|
||||
}
|
||||
|
||||
slot_ = slot;
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, slot_, ubo_id_);
|
||||
|
||||
#ifndef NDEBUG
|
||||
BLI_assert(slot < 16);
|
||||
GLContext::get()->bound_ubo_slots |= 1 << slot;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLUniformBuf::bind_as_ssbo(int slot)
|
||||
{
|
||||
if (ubo_id_ == 0) {
|
||||
this->init();
|
||||
}
|
||||
if (data_ != nullptr) {
|
||||
this->update(data_);
|
||||
MEM_SAFE_DELETE_VOID(data_);
|
||||
}
|
||||
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, ubo_id_);
|
||||
#ifndef NDEBUG
|
||||
BLI_assert(slot < 16);
|
||||
GLContext::get()->bound_ssbo_slots |= 1 << slot;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLUniformBuf::unbind()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
/* NOTE: This only unbinds the last bound slot. */
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, slot_, 0);
|
||||
/* Hope that the context did not change. */
|
||||
GLContext::get()->bound_ubo_slots &= ~(1 << slot_);
|
||||
#endif
|
||||
slot_ = 0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
43
blender-5.2.0/source/blender/gpu/opengl/gl_uniform_buffer.hh
Normal file
43
blender-5.2.0/source/blender/gpu/opengl/gl_uniform_buffer.hh
Normal file
@@ -0,0 +1,43 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "gpu_uniform_buffer_private.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/**
|
||||
* Implementation of Uniform Buffers using OpenGL.
|
||||
*/
|
||||
class GLUniformBuf : public UniformBuf {
|
||||
private:
|
||||
/** Slot to which this UBO is currently bound. -1 if not bound. */
|
||||
int slot_ = -1;
|
||||
/** OpenGL Object handle. */
|
||||
GLuint ubo_id_ = 0;
|
||||
|
||||
public:
|
||||
GLUniformBuf(size_t size, const char *name);
|
||||
~GLUniformBuf();
|
||||
|
||||
void update(const void *data) override;
|
||||
void clear_to_zero() override;
|
||||
void bind(int slot) override;
|
||||
void bind_as_ssbo(int slot) override;
|
||||
void unbind() override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLUniformBuf");
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
132
blender-5.2.0/source/blender/gpu/opengl/gl_vertex_array.cc
Normal file
132
blender-5.2.0/source/blender/gpu/opengl/gl_vertex_array.cc
Normal file
@@ -0,0 +1,132 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
#include "gpu_shader_interface.hh"
|
||||
|
||||
#include "gl_batch.hh"
|
||||
#include "gl_context.hh"
|
||||
#include "gl_index_buffer.hh"
|
||||
#include "gl_storage_buffer.hh"
|
||||
#include "gl_vertex_buffer.hh"
|
||||
|
||||
#include "gl_vertex_array.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Vertex Array Bindings
|
||||
* \{ */
|
||||
|
||||
/** Returns enabled vertex pointers as a bit-flag (one bit per attribute). */
|
||||
static uint16_t vbo_bind(const ShaderInterface *interface,
|
||||
const GPUVertFormat *format,
|
||||
uint v_first,
|
||||
uint v_len,
|
||||
const bool use_instancing)
|
||||
{
|
||||
uint16_t enabled_attrib = 0;
|
||||
const uint attr_len = format->attr_len;
|
||||
uint stride = format->stride;
|
||||
uint offset = 0;
|
||||
GLuint divisor = (use_instancing) ? 1 : 0;
|
||||
|
||||
for (uint a_idx = 0; a_idx < attr_len; a_idx++) {
|
||||
const GPUVertAttr *a = &format->attrs[a_idx];
|
||||
|
||||
if (format->deinterleaved) {
|
||||
offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].type.size()) * v_len;
|
||||
stride = a->type.size();
|
||||
}
|
||||
else {
|
||||
offset = a->offset;
|
||||
}
|
||||
|
||||
/* This is in fact an offset in memory. */
|
||||
const GLvoid *pointer = reinterpret_cast<const GLubyte *>(intptr_t(offset + v_first * stride));
|
||||
const GLenum type = to_gl(a->type.comp_type());
|
||||
|
||||
for (uint n_idx = 0; n_idx < a->name_len; n_idx++) {
|
||||
const char *name = GPU_vertformat_attr_name_get(format, a, n_idx);
|
||||
const ShaderInput *input = interface->attr_get(name);
|
||||
|
||||
if (input == nullptr || input->location == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
enabled_attrib |= (1 << input->location);
|
||||
|
||||
glEnableVertexAttribArray(input->location);
|
||||
glVertexAttribDivisor(input->location, divisor);
|
||||
|
||||
switch (a->type.fetch_mode()) {
|
||||
case GPU_FETCH_FLOAT:
|
||||
case GPU_FETCH_INT_TO_FLOAT_UNIT:
|
||||
glVertexAttribPointer(
|
||||
input->location, a->type.comp_len(), type, GL_TRUE, stride, pointer);
|
||||
break;
|
||||
case GPU_FETCH_INT:
|
||||
glVertexAttribIPointer(input->location, a->type.comp_len(), type, stride, pointer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return enabled_attrib;
|
||||
}
|
||||
|
||||
void GLVertArray::update_bindings(const GLuint vao,
|
||||
const Batch *batch_, /* Should be GLBatch. */
|
||||
const ShaderInterface *interface)
|
||||
{
|
||||
const GLBatch *batch = static_cast<const GLBatch *>(batch_);
|
||||
uint16_t attr_mask = interface->enabled_attr_mask_;
|
||||
|
||||
glBindVertexArray(vao);
|
||||
|
||||
/* Reverse order so first VBO'S have more prevalence (in term of attribute override). */
|
||||
for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) {
|
||||
GLVertBuf *vbo = batch->verts_(v);
|
||||
if (vbo) {
|
||||
vbo->bind();
|
||||
attr_mask &= ~vbo_bind(interface, &vbo->format, 0, vbo->vertex_len, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (attr_mask != 0) {
|
||||
for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) {
|
||||
if (attr_mask & mask) {
|
||||
GLContext *ctx = GLContext::get();
|
||||
/* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style.
|
||||
* Fix issues for some drivers (see #75069). */
|
||||
glBindVertexBuffer(a, ctx->default_attr_vbo_, intptr_t(0), intptr_t(0));
|
||||
glEnableVertexAttribArray(a);
|
||||
glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0);
|
||||
glVertexAttribBinding(a, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (batch->elem) {
|
||||
/* Binds the index buffer. This state is also saved in the VAO. */
|
||||
static_cast<GLIndexBuf *>(batch->elem)->bind();
|
||||
}
|
||||
}
|
||||
|
||||
void GLVertArray::update_bindings(const GLuint vao,
|
||||
const uint v_first,
|
||||
const GPUVertFormat *format,
|
||||
const ShaderInterface *interface)
|
||||
{
|
||||
glBindVertexArray(vao);
|
||||
|
||||
vbo_bind(interface, format, v_first, 0, false);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::gpu
|
||||
29
blender-5.2.0/source/blender/gpu/opengl/gl_vertex_array.hh
Normal file
29
blender-5.2.0/source/blender/gpu/opengl/gl_vertex_array.hh
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "gl_shader_interface.hh"
|
||||
|
||||
namespace blender::gpu::GLVertArray {
|
||||
|
||||
/**
|
||||
* Update the Attribute Binding of the currently bound VAO.
|
||||
*/
|
||||
void update_bindings(const GLuint vao, const Batch *batch, const ShaderInterface *interface);
|
||||
|
||||
/**
|
||||
* Another version of update_bindings for Immediate mode.
|
||||
*/
|
||||
void update_bindings(const GLuint vao,
|
||||
uint v_first,
|
||||
const GPUVertFormat *format,
|
||||
const ShaderInterface *interface);
|
||||
|
||||
} // namespace blender::gpu::GLVertArray
|
||||
145
blender-5.2.0/source/blender/gpu/opengl/gl_vertex_buffer.cc
Normal file
145
blender-5.2.0/source/blender/gpu/opengl/gl_vertex_buffer.cc
Normal file
@@ -0,0 +1,145 @@
|
||||
/* SPDX-FileCopyrightText: 2016 by Mike Erwin. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "gl_context.hh"
|
||||
|
||||
#include "gl_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
void GLVertBuf::acquire_data()
|
||||
{
|
||||
if (usage_ == GPU_USAGE_DEVICE_ONLY) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Discard previous data if any. */
|
||||
MEM_SAFE_DELETE(data_);
|
||||
data_ = MEM_new_array_uninitialized<uchar>(this->size_alloc_get(), __func__);
|
||||
}
|
||||
|
||||
void GLVertBuf::resize_data()
|
||||
{
|
||||
if (usage_ == GPU_USAGE_DEVICE_ONLY) {
|
||||
return;
|
||||
}
|
||||
|
||||
data_ = static_cast<uchar *>(
|
||||
MEM_realloc_uninitialized(data_, sizeof(uchar) * this->size_alloc_get()));
|
||||
}
|
||||
|
||||
void GLVertBuf::release_data()
|
||||
{
|
||||
if (is_wrapper_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vbo_id_ != 0) {
|
||||
GPU_TEXTURE_FREE_SAFE(buffer_texture_);
|
||||
GLContext::buffer_free(vbo_id_);
|
||||
vbo_id_ = 0;
|
||||
memory_usage -= vbo_size_;
|
||||
}
|
||||
|
||||
MEM_SAFE_DELETE(data_);
|
||||
}
|
||||
|
||||
void GLVertBuf::upload_data()
|
||||
{
|
||||
this->bind();
|
||||
}
|
||||
|
||||
void GLVertBuf::bind()
|
||||
{
|
||||
BLI_assert(GLContext::get() != nullptr);
|
||||
|
||||
if (vbo_id_ == 0) {
|
||||
glGenBuffers(1, &vbo_id_);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_id_);
|
||||
|
||||
if (flag & GPU_VERTBUF_DATA_DIRTY) {
|
||||
vbo_size_ = this->size_used_get();
|
||||
|
||||
/* This is fine on some systems but will crash on others. */
|
||||
BLI_assert(vbo_size_ != 0);
|
||||
/* Orphan the vbo to avoid sync then upload data. */
|
||||
glBufferData(GL_ARRAY_BUFFER, ceil_to_multiple_ul(vbo_size_, 16), nullptr, to_gl(usage_));
|
||||
/* Do not transfer data from host to device when buffer is device only. */
|
||||
if (usage_ != GPU_USAGE_DEVICE_ONLY) {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data_);
|
||||
}
|
||||
memory_usage += vbo_size_;
|
||||
|
||||
if (usage_ == GPU_USAGE_STATIC) {
|
||||
MEM_SAFE_DELETE(data_);
|
||||
}
|
||||
flag &= ~GPU_VERTBUF_DATA_DIRTY;
|
||||
flag |= GPU_VERTBUF_DATA_UPLOADED;
|
||||
}
|
||||
}
|
||||
|
||||
void GLVertBuf::bind_as_ssbo(uint binding)
|
||||
{
|
||||
bind();
|
||||
BLI_assert(vbo_id_ != 0);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_);
|
||||
|
||||
#ifndef NDEBUG
|
||||
BLI_assert(binding < 16);
|
||||
GLContext::get()->bound_ssbo_slots |= 1 << binding;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLVertBuf::bind_as_texture(uint binding)
|
||||
{
|
||||
bind();
|
||||
BLI_assert(vbo_id_ != 0);
|
||||
if (buffer_texture_ == nullptr) {
|
||||
buffer_texture_ = GPU_texture_create_from_vertbuf("vertbuf_as_texture", this);
|
||||
}
|
||||
GPU_texture_bind(buffer_texture_, binding);
|
||||
}
|
||||
|
||||
void GLVertBuf::read(void *data) const
|
||||
{
|
||||
BLI_assert(is_active());
|
||||
void *result = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
|
||||
memcpy(data, result, size_used_get());
|
||||
glUnmapBuffer(GL_ARRAY_BUFFER);
|
||||
}
|
||||
|
||||
void GLVertBuf::wrap_handle(uint64_t handle)
|
||||
{
|
||||
BLI_assert(vbo_id_ == 0);
|
||||
BLI_assert(glIsBuffer(uint(handle)));
|
||||
is_wrapper_ = true;
|
||||
vbo_id_ = uint(handle);
|
||||
/* We assume the data is already on the device, so no need to allocate or send it. */
|
||||
flag = GPU_VERTBUF_DATA_UPLOADED;
|
||||
}
|
||||
|
||||
bool GLVertBuf::is_active() const
|
||||
{
|
||||
if (!vbo_id_) {
|
||||
return false;
|
||||
}
|
||||
int active_vbo_id = 0;
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &active_vbo_id);
|
||||
return vbo_id_ == active_vbo_id;
|
||||
}
|
||||
|
||||
void GLVertBuf::update_sub(uint start, uint len, const void *data)
|
||||
{
|
||||
glBufferSubData(GL_ARRAY_BUFFER, start, len, data);
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
98
blender-5.2.0/source/blender/gpu/opengl/gl_vertex_buffer.hh
Normal file
98
blender-5.2.0/source/blender/gpu/opengl/gl_vertex_buffer.hh
Normal file
@@ -0,0 +1,98 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
|
||||
class GLVertBuf : public VertBuf {
|
||||
friend class GLTexture; /* For buffer texture. */
|
||||
friend class GLStorageBuf; /* For sub copy. */
|
||||
|
||||
private:
|
||||
/** OpenGL buffer handle. Init on first upload. Immutable after that. */
|
||||
GLuint vbo_id_ = 0;
|
||||
/** Texture used if the buffer is bound as buffer texture. Init on first use. */
|
||||
gpu::Texture *buffer_texture_ = nullptr;
|
||||
/** Defines whether the buffer handle is wrapped by this GLVertBuf, i.e. we do not own it and
|
||||
* should not free it. */
|
||||
bool is_wrapper_ = false;
|
||||
/** Size on the GPU. */
|
||||
size_t vbo_size_ = 0;
|
||||
|
||||
public:
|
||||
void bind();
|
||||
|
||||
void update_sub(uint start, uint len, const void *data) override;
|
||||
|
||||
void read(void *data) const override;
|
||||
|
||||
void wrap_handle(uint64_t handle) override;
|
||||
|
||||
protected:
|
||||
void acquire_data() override;
|
||||
void resize_data() override;
|
||||
void release_data() override;
|
||||
void upload_data() override;
|
||||
void bind_as_ssbo(uint binding) override;
|
||||
void bind_as_texture(uint binding) override;
|
||||
|
||||
private:
|
||||
bool is_active() const;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLVertBuf");
|
||||
};
|
||||
|
||||
static inline GLenum to_gl(GPUUsageType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_USAGE_STREAM:
|
||||
return GL_STREAM_DRAW;
|
||||
case GPU_USAGE_DYNAMIC:
|
||||
return GL_DYNAMIC_DRAW;
|
||||
case GPU_USAGE_STATIC:
|
||||
case GPU_USAGE_DEVICE_ONLY:
|
||||
return GL_STATIC_DRAW;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return GL_STATIC_DRAW;
|
||||
}
|
||||
}
|
||||
|
||||
static inline GLenum to_gl(GPUVertCompType type)
|
||||
{
|
||||
switch (type) {
|
||||
case GPU_COMP_I8:
|
||||
return GL_BYTE;
|
||||
case GPU_COMP_U8:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case GPU_COMP_I16:
|
||||
return GL_SHORT;
|
||||
case GPU_COMP_U16:
|
||||
return GL_UNSIGNED_SHORT;
|
||||
case GPU_COMP_I32:
|
||||
return GL_INT;
|
||||
case GPU_COMP_U32:
|
||||
return GL_UNSIGNED_INT;
|
||||
case GPU_COMP_F32:
|
||||
return GL_FLOAT;
|
||||
case GPU_COMP_I10:
|
||||
return GL_INT_2_10_10_10_REV;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
return GL_FLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
||||
Reference in New Issue
Block a user