This covers implementation of the GPUTexture abstraction for the Metal backend, with additional utility functionality as required. Some components have been temporarily disabled pending dependencies on upcoming Metal backend components, and these will be addressed as the backend is fleshed out. One core challenge addressed in the Metal backend is the requirement for read/update routines for textures. MTLBlitCommandEncoders offer a limited range of the full functionality provided by OpenGLs texture update and read functions such that a series of compute kernels have been implemented to provide advanced functionality such as data format conversion and partial/swizzled component updates. This diff is provided in full, but if further division is required for purposes of code review, this can be done. Authored by Apple: Michael Parkin-White Ref T96261 Reviewed By: fclem Maniphest Tasks: T96261 Differential Revision: https://developer.blender.org/D14543
411 lines
9.8 KiB
C++
411 lines
9.8 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#ifndef GPU_STANDALONE
|
|
# include "DNA_userdef_types.h"
|
|
# define PIXELSIZE (U.pixelsize)
|
|
#else
|
|
# define PIXELSIZE (1.0f)
|
|
#endif
|
|
|
|
#include "BLI_math_vector.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_global.h"
|
|
|
|
#include "GPU_state.h"
|
|
|
|
#include "gpu_context_private.hh"
|
|
|
|
#include "gpu_state_private.hh"
|
|
|
|
using namespace blender::gpu;
|
|
|
|
#define SET_STATE(_prefix, _state, _value) \
|
|
do { \
|
|
StateManager *stack = Context::get()->state_manager; \
|
|
auto &state_object = stack->_prefix##state; \
|
|
state_object._state = (_value); \
|
|
} while (0)
|
|
|
|
#define SET_IMMUTABLE_STATE(_state, _value) SET_STATE(, _state, _value)
|
|
#define SET_MUTABLE_STATE(_state, _value) SET_STATE(mutable_, _state, _value)
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Immutable state Setters
|
|
* \{ */
|
|
|
|
void GPU_blend(eGPUBlend blend)
|
|
{
|
|
SET_IMMUTABLE_STATE(blend, blend);
|
|
}
|
|
|
|
void GPU_face_culling(eGPUFaceCullTest culling)
|
|
{
|
|
SET_IMMUTABLE_STATE(culling_test, culling);
|
|
}
|
|
|
|
eGPUFaceCullTest GPU_face_culling_get()
|
|
{
|
|
GPUState &state = Context::get()->state_manager->state;
|
|
return (eGPUFaceCullTest)state.culling_test;
|
|
}
|
|
|
|
void GPU_front_facing(bool invert)
|
|
{
|
|
SET_IMMUTABLE_STATE(invert_facing, invert);
|
|
}
|
|
|
|
void GPU_provoking_vertex(eGPUProvokingVertex vert)
|
|
{
|
|
SET_IMMUTABLE_STATE(provoking_vert, vert);
|
|
}
|
|
|
|
void GPU_depth_test(eGPUDepthTest test)
|
|
{
|
|
SET_IMMUTABLE_STATE(depth_test, test);
|
|
}
|
|
|
|
void GPU_stencil_test(eGPUStencilTest test)
|
|
{
|
|
SET_IMMUTABLE_STATE(stencil_test, test);
|
|
}
|
|
|
|
void GPU_line_smooth(bool enable)
|
|
{
|
|
SET_IMMUTABLE_STATE(line_smooth, enable);
|
|
}
|
|
|
|
void GPU_polygon_smooth(bool enable)
|
|
{
|
|
SET_IMMUTABLE_STATE(polygon_smooth, enable);
|
|
}
|
|
|
|
void GPU_logic_op_xor_set(bool enable)
|
|
{
|
|
SET_IMMUTABLE_STATE(logic_op_xor, enable);
|
|
}
|
|
|
|
void GPU_write_mask(eGPUWriteMask mask)
|
|
{
|
|
SET_IMMUTABLE_STATE(write_mask, mask);
|
|
}
|
|
|
|
void GPU_color_mask(bool r, bool g, bool b, bool a)
|
|
{
|
|
StateManager *stack = Context::get()->state_manager;
|
|
auto &state = stack->state;
|
|
uint32_t write_mask = state.write_mask;
|
|
SET_FLAG_FROM_TEST(write_mask, r, (uint32_t)GPU_WRITE_RED);
|
|
SET_FLAG_FROM_TEST(write_mask, g, (uint32_t)GPU_WRITE_GREEN);
|
|
SET_FLAG_FROM_TEST(write_mask, b, (uint32_t)GPU_WRITE_BLUE);
|
|
SET_FLAG_FROM_TEST(write_mask, a, (uint32_t)GPU_WRITE_ALPHA);
|
|
state.write_mask = write_mask;
|
|
}
|
|
|
|
void GPU_depth_mask(bool depth)
|
|
{
|
|
StateManager *stack = Context::get()->state_manager;
|
|
auto &state = stack->state;
|
|
uint32_t write_mask = state.write_mask;
|
|
SET_FLAG_FROM_TEST(write_mask, depth, (uint32_t)GPU_WRITE_DEPTH);
|
|
state.write_mask = write_mask;
|
|
}
|
|
|
|
void GPU_shadow_offset(bool enable)
|
|
{
|
|
SET_IMMUTABLE_STATE(shadow_bias, enable);
|
|
}
|
|
|
|
void GPU_clip_distances(int distances_enabled)
|
|
{
|
|
SET_IMMUTABLE_STATE(clip_distances, distances_enabled);
|
|
}
|
|
|
|
void GPU_state_set(eGPUWriteMask write_mask,
|
|
eGPUBlend blend,
|
|
eGPUFaceCullTest culling_test,
|
|
eGPUDepthTest depth_test,
|
|
eGPUStencilTest stencil_test,
|
|
eGPUStencilOp stencil_op,
|
|
eGPUProvokingVertex provoking_vert)
|
|
{
|
|
StateManager *stack = Context::get()->state_manager;
|
|
auto &state = stack->state;
|
|
state.write_mask = (uint32_t)write_mask;
|
|
state.blend = (uint32_t)blend;
|
|
state.culling_test = (uint32_t)culling_test;
|
|
state.depth_test = (uint32_t)depth_test;
|
|
state.stencil_test = (uint32_t)stencil_test;
|
|
state.stencil_op = (uint32_t)stencil_op;
|
|
state.provoking_vert = (uint32_t)provoking_vert;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Mutable State Setters
|
|
* \{ */
|
|
|
|
void GPU_depth_range(float near, float far)
|
|
{
|
|
StateManager *stack = Context::get()->state_manager;
|
|
auto &state = stack->mutable_state;
|
|
copy_v2_fl2(state.depth_range, near, far);
|
|
}
|
|
|
|
void GPU_line_width(float width)
|
|
{
|
|
width = max_ff(1.0f, width * PIXELSIZE);
|
|
SET_MUTABLE_STATE(line_width, width);
|
|
}
|
|
|
|
void GPU_point_size(float size)
|
|
{
|
|
StateManager *stack = Context::get()->state_manager;
|
|
auto &state = stack->mutable_state;
|
|
/* Keep the sign of point_size since it represents the enable state. */
|
|
state.point_size = size * ((state.point_size > 0.0) ? 1.0f : -1.0f);
|
|
}
|
|
|
|
void GPU_program_point_size(bool enable)
|
|
{
|
|
StateManager *stack = Context::get()->state_manager;
|
|
auto &state = stack->mutable_state;
|
|
/* Set point size sign negative to disable. */
|
|
state.point_size = fabsf(state.point_size) * (enable ? 1 : -1);
|
|
}
|
|
|
|
void GPU_scissor_test(bool enable)
|
|
{
|
|
Context::get()->active_fb->scissor_test_set(enable);
|
|
}
|
|
|
|
void GPU_scissor(int x, int y, int width, int height)
|
|
{
|
|
int scissor_rect[4] = {x, y, width, height};
|
|
Context::get()->active_fb->scissor_set(scissor_rect);
|
|
}
|
|
|
|
void GPU_viewport(int x, int y, int width, int height)
|
|
{
|
|
int viewport_rect[4] = {x, y, width, height};
|
|
Context::get()->active_fb->viewport_set(viewport_rect);
|
|
}
|
|
|
|
void GPU_stencil_reference_set(uint reference)
|
|
{
|
|
SET_MUTABLE_STATE(stencil_reference, (uint8_t)reference);
|
|
}
|
|
|
|
void GPU_stencil_write_mask_set(uint write_mask)
|
|
{
|
|
SET_MUTABLE_STATE(stencil_write_mask, (uint8_t)write_mask);
|
|
}
|
|
|
|
void GPU_stencil_compare_mask_set(uint compare_mask)
|
|
{
|
|
SET_MUTABLE_STATE(stencil_compare_mask, (uint8_t)compare_mask);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name State Getters
|
|
* \{ */
|
|
|
|
eGPUBlend GPU_blend_get()
|
|
{
|
|
GPUState &state = Context::get()->state_manager->state;
|
|
return (eGPUBlend)state.blend;
|
|
}
|
|
|
|
eGPUWriteMask GPU_write_mask_get()
|
|
{
|
|
GPUState &state = Context::get()->state_manager->state;
|
|
return (eGPUWriteMask)state.write_mask;
|
|
}
|
|
|
|
uint GPU_stencil_mask_get()
|
|
{
|
|
const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
|
|
return state.stencil_write_mask;
|
|
}
|
|
|
|
eGPUDepthTest GPU_depth_test_get()
|
|
{
|
|
GPUState &state = Context::get()->state_manager->state;
|
|
return (eGPUDepthTest)state.depth_test;
|
|
}
|
|
|
|
eGPUStencilTest GPU_stencil_test_get()
|
|
{
|
|
GPUState &state = Context::get()->state_manager->state;
|
|
return (eGPUStencilTest)state.stencil_test;
|
|
}
|
|
|
|
float GPU_line_width_get()
|
|
{
|
|
const GPUStateMutable &state = Context::get()->state_manager->mutable_state;
|
|
return state.line_width;
|
|
}
|
|
|
|
void GPU_scissor_get(int coords[4])
|
|
{
|
|
Context::get()->active_fb->scissor_get(coords);
|
|
}
|
|
|
|
void GPU_viewport_size_get_f(float coords[4])
|
|
{
|
|
int viewport[4];
|
|
Context::get()->active_fb->viewport_get(viewport);
|
|
for (int i = 0; i < 4; i++) {
|
|
coords[i] = viewport[i];
|
|
}
|
|
}
|
|
|
|
void GPU_viewport_size_get_i(int coords[4])
|
|
{
|
|
Context::get()->active_fb->viewport_get(coords);
|
|
}
|
|
|
|
bool GPU_depth_mask_get()
|
|
{
|
|
const GPUState &state = Context::get()->state_manager->state;
|
|
return (state.write_mask & GPU_WRITE_DEPTH) != 0;
|
|
}
|
|
|
|
bool GPU_mipmap_enabled()
|
|
{
|
|
/* TODO(fclem): this used to be a userdef option. */
|
|
return true;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Context Utils
|
|
* \{ */
|
|
|
|
void GPU_flush()
|
|
{
|
|
Context::get()->flush();
|
|
}
|
|
|
|
void GPU_finish()
|
|
{
|
|
Context::get()->finish();
|
|
}
|
|
|
|
void GPU_apply_state()
|
|
{
|
|
Context::get()->state_manager->apply_state();
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name BGL workaround
|
|
*
|
|
* bgl makes direct GL calls that makes our state tracking out of date.
|
|
* This flag make it so that the pyGPU calls will not override the state set by
|
|
* bgl functions.
|
|
* \{ */
|
|
|
|
void GPU_bgl_start()
|
|
{
|
|
Context *ctx = Context::get();
|
|
if (!(ctx && ctx->state_manager)) {
|
|
return;
|
|
}
|
|
StateManager &state_manager = *(Context::get()->state_manager);
|
|
if (state_manager.use_bgl == false) {
|
|
/* Expected by many addons (see T80169, T81289).
|
|
* This will reset the blend function. */
|
|
GPU_blend(GPU_BLEND_NONE);
|
|
|
|
/* Equivalent of setting the depth func `glDepthFunc(GL_LEQUAL)`
|
|
* Needed since Python scripts may enable depth test.
|
|
* Without this block the depth test function is undefined. */
|
|
{
|
|
eGPUDepthTest depth_test_real = GPU_depth_test_get();
|
|
eGPUDepthTest depth_test_temp = GPU_DEPTH_LESS_EQUAL;
|
|
if (depth_test_real != depth_test_temp) {
|
|
GPU_depth_test(depth_test_temp);
|
|
state_manager.apply_state();
|
|
GPU_depth_test(depth_test_real);
|
|
}
|
|
}
|
|
|
|
state_manager.apply_state();
|
|
state_manager.use_bgl = true;
|
|
}
|
|
}
|
|
|
|
void GPU_bgl_end()
|
|
{
|
|
Context *ctx = Context::get();
|
|
if (!(ctx && ctx->state_manager)) {
|
|
return;
|
|
}
|
|
StateManager &state_manager = *ctx->state_manager;
|
|
if (state_manager.use_bgl == true) {
|
|
state_manager.use_bgl = false;
|
|
/* Resync state tracking. */
|
|
state_manager.force_state();
|
|
}
|
|
}
|
|
|
|
bool GPU_bgl_get()
|
|
{
|
|
return Context::get()->state_manager->use_bgl;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Synchronization Utils
|
|
* \{ */
|
|
|
|
void GPU_memory_barrier(eGPUBarrier barrier)
|
|
{
|
|
Context::get()->state_manager->issue_barrier(barrier);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Default State
|
|
* \{ */
|
|
|
|
StateManager::StateManager()
|
|
{
|
|
/* Set default state. */
|
|
state.write_mask = GPU_WRITE_COLOR;
|
|
state.blend = GPU_BLEND_NONE;
|
|
state.culling_test = GPU_CULL_NONE;
|
|
state.depth_test = GPU_DEPTH_NONE;
|
|
state.stencil_test = GPU_STENCIL_NONE;
|
|
state.stencil_op = GPU_STENCIL_OP_NONE;
|
|
state.provoking_vert = GPU_VERTEX_LAST;
|
|
state.logic_op_xor = false;
|
|
state.invert_facing = false;
|
|
state.shadow_bias = false;
|
|
state.clip_distances = 0;
|
|
state.polygon_smooth = false;
|
|
state.line_smooth = false;
|
|
|
|
mutable_state.depth_range[0] = 0.0f;
|
|
mutable_state.depth_range[1] = 1.0f;
|
|
mutable_state.point_size = -1.0f; /* Negative is not using point size. */
|
|
mutable_state.line_width = 1.0f;
|
|
mutable_state.stencil_write_mask = 0x00;
|
|
mutable_state.stencil_compare_mask = 0x00;
|
|
mutable_state.stencil_reference = 0x00;
|
|
}
|
|
|
|
/** \} */
|