diff options
author | Omar Emara <mail@OmarEmara.dev> | 2022-05-20 14:11:03 +0300 |
---|---|---|
committer | Omar Emara <mail@OmarEmara.dev> | 2022-05-20 14:11:03 +0300 |
commit | 30132dec018a669eeb28aba072991581c8ca61bd (patch) | |
tree | 73ccdfa51e8a7a5b660fe5416f6feecc78a5ea87 /source/blender/gpu | |
parent | 48006f8b5f72b233986f83bbd20a4370c6afd89d (diff) | |
parent | a89f829f12f56214b0e463b33f24edf27228db1f (diff) |
Merge branch 'master' into temp-viewport-compositor-merge
Diffstat (limited to 'source/blender/gpu')
30 files changed, 1435 insertions, 166 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index a2db89f849e..bf2f52dfa7a 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -85,6 +85,7 @@ set(SRC GPU_buffers.h GPU_capabilities.h GPU_common.h + GPU_common_types.h GPU_compute.h GPU_context.h GPU_debug.h @@ -189,6 +190,7 @@ set(METAL_SRC metal/mtl_backend.mm metal/mtl_context.mm metal/mtl_debug.mm + metal/mtl_state.mm metal/mtl_texture.mm metal/mtl_texture_util.mm @@ -197,6 +199,7 @@ set(METAL_SRC metal/mtl_common.hh metal/mtl_context.hh metal/mtl_debug.hh + metal/mtl_state.hh metal/mtl_texture.hh ) @@ -301,8 +304,6 @@ set(GLSL_SRC shaders/gpu_shader_codegen_lib.glsl - shaders/gpu_shader_geometry.glsl - shaders/common/gpu_shader_common_color_ramp.glsl shaders/common/gpu_shader_common_color_utils.glsl shaders/common/gpu_shader_common_curves.glsl @@ -483,6 +484,7 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) set(SRC_SHADER_CREATE_INFOS ../draw/engines/basic/shaders/infos/basic_depth_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh ../draw/engines/overlay/shaders/infos/antialiasing_info.hh diff --git a/source/blender/gpu/GPU_common_types.h b/source/blender/gpu/GPU_common_types.h new file mode 100644 index 00000000000..8c91d60812f --- /dev/null +++ b/source/blender/gpu/GPU_common_types.h @@ -0,0 +1,18 @@ +/** \file + * \ingroup gpu + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum eGPUFrontFace { + GPU_CLOCKWISE, + GPU_COUNTERCLOCKWISE, +} eGPUFrontFace; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_legacy_stubs.h b/source/blender/gpu/GPU_legacy_stubs.h index 369347447f8..5970738a9b3 100644 --- a/source/blender/gpu/GPU_legacy_stubs.h +++ b/source/blender/gpu/GPU_legacy_stubs.h @@ -23,7 +23,7 @@ #include "BLI_utildefines.h" /** - * Empty function, use for breakpoint when a deprecated + * Empty function, use for break-point when a deprecated * OpenGL function is called. */ static void gl_deprecated(void) diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 99b60351dcc..7519b3bc1cb 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -35,6 +35,19 @@ typedef enum eGPUBarrier { ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_ELEMENT_ARRAY) +/* NOTE: For Metal and Vulkan only. + * TODO(Metal): Update barrier calls to use stage flags. */ +typedef enum eGPUStageBarrierBits { + GPU_BARRIER_STAGE_VERTEX = (1 << 0), + GPU_BARRIER_STAGE_FRAGMENT = (1 << 1), + GPU_BARRIER_STAGE_COMPUTE = (1 << 2), + GPU_BARRIER_STAGE_ANY_GRAPHICS = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT), + GPU_BARRIER_STAGE_ANY = (GPU_BARRIER_STAGE_VERTEX | GPU_BARRIER_STAGE_FRAGMENT | + GPU_BARRIER_STAGE_COMPUTE), +} eGPUStageBarrierBits; + +ENUM_OPERATORS(eGPUStageBarrierBits, GPU_BARRIER_STAGE_COMPUTE) + /** * Defines the fixed pipeline blending equation. * SRC is the output color from the shader. diff --git a/source/blender/gpu/GPU_storage_buffer.h b/source/blender/gpu/GPU_storage_buffer.h index 1478d490e23..ca6a848786b 100644 --- a/source/blender/gpu/GPU_storage_buffer.h +++ b/source/blender/gpu/GPU_storage_buffer.h @@ -47,6 +47,18 @@ void GPU_storagebuf_clear(GPUStorageBuf *ssbo, void *data); void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo); +/** + * \brief Copy a part of a vertex buffer to a storage buffer. + * + * \param ssbo: destination storage buffer + * \param src: source vertex buffer + * \param dst_offset: where to start copying to (in bytes). + * \param src_offset: where to start copying from (in bytes). + * \param copy_size: byte size of the segment to copy. + */ +void GPU_storagebuf_copy_sub_from_vertbuf( + GPUStorageBuf *ssbo, GPUVertBuf *src, uint dst_offset, uint src_offset, uint copy_size); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index fe6ed7eaf87..722ef878271 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -165,6 +165,7 @@ void GPU_vertbuf_tag_dirty(GPUVertBuf *verts); */ void GPU_vertbuf_use(GPUVertBuf *); void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding); +void GPU_vertbuf_bind_as_texture(struct GPUVertBuf *verts, int binding); void GPU_vertbuf_wrap_handle(GPUVertBuf *verts, uint64_t handle); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index e7337073773..964a1a51361 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -643,7 +643,7 @@ eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat) return mat->flag; } -/* Note: Consumes the flags. */ +/* NOTE: Consumes the flags. */ bool GPU_material_recalc_flag_get(GPUMaterial *mat) { bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0; diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index ac9f13a3343..8d53a170d22 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -695,14 +695,20 @@ Vector<const char *> gpu_shader_dependency_get_resolved_source( const StringRefNull shader_source_name) { Vector<const char *> result; - GPUSource *source = g_sources->lookup(shader_source_name); - source->build(result); + GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr); + if (src == nullptr) { + std::cout << "Error source not found : " << shader_source_name << std::endl; + } + src->build(result); return result; } StringRefNull gpu_shader_dependency_get_source(const StringRefNull shader_source_name) { - GPUSource *src = g_sources->lookup(shader_source_name); + GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr); + if (src == nullptr) { + std::cout << "Error source not found : " << shader_source_name << std::endl; + } return src->source; } diff --git a/source/blender/gpu/intern/gpu_storage_buffer.cc b/source/blender/gpu/intern/gpu_storage_buffer.cc index 806acad90fe..68020ec66f4 100644 --- a/source/blender/gpu/intern/gpu_storage_buffer.cc +++ b/source/blender/gpu/intern/gpu_storage_buffer.cc @@ -19,6 +19,7 @@ #include "GPU_storage_buffer.h" #include "gpu_storage_buffer_private.hh" +#include "gpu_vertex_buffer_private.hh" /* -------------------------------------------------------------------- */ /** \name Creation & Deletion @@ -103,4 +104,10 @@ void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo) GPU_storagebuf_clear(ssbo, GPU_R32UI, GPU_DATA_UINT, &data); } +void GPU_storagebuf_copy_sub_from_vertbuf( + GPUStorageBuf *ssbo, GPUVertBuf *src, uint dst_offset, uint src_offset, uint copy_size) +{ + unwrap(ssbo)->copy_sub(unwrap(src), dst_offset, src_offset, copy_size); +} + /** \} */ diff --git a/source/blender/gpu/intern/gpu_storage_buffer_private.hh b/source/blender/gpu/intern/gpu_storage_buffer_private.hh index 06e7c2c0e9d..091e6c2d386 100644 --- a/source/blender/gpu/intern/gpu_storage_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_storage_buffer_private.hh @@ -43,6 +43,7 @@ class StorageBuf { virtual void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) = 0; + virtual void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) = 0; }; /* Syntactic sugar. */ diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 13f409cfba5..f47970d48d1 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -328,6 +328,11 @@ void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding) unwrap(verts)->bind_as_ssbo(binding); } +void GPU_vertbuf_bind_as_texture(struct GPUVertBuf *verts, int binding) +{ + unwrap(verts)->bind_as_texture(binding); +} + void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, const void *data) { unwrap(verts)->update_sub(start, len, data); diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh index e5b70de9dfa..7a0b53cf958 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh @@ -51,6 +51,7 @@ class VertBuf { void resize(uint vert_len); void upload(); virtual void bind_as_ssbo(uint binding) = 0; + virtual void bind_as_texture(uint binding) = 0; virtual void wrap_handle(uint64_t handle) = 0; diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl index ecb2dddcd63..9fd54f3f31f 100644 --- a/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl +++ b/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl @@ -7,4 +7,4 @@ in vec2 texCoord_interp; void main() { gl_FragDepth = textureLod(source_data, texCoord_interp, mip).r; -}
\ No newline at end of file +} diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl index 99661a760f0..7483343503f 100644 --- a/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl +++ b/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl @@ -10,4 +10,4 @@ void main() uint stencil = (val >> 24) & 0xFFu; uint depth = (val)&0xFFFFFFu; gl_FragDepth = float(depth) / float(0xFFFFFFu); -}
\ No newline at end of file +} diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl index 15271ab2cdd..75d42c57f73 100644 --- a/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl +++ b/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl @@ -9,4 +9,4 @@ void main() uint val = textureLod(source_data, texCoord_interp, mip).r; uint depth = (val) & (0xFFFFFFFFu); gl_FragDepth = float(depth) / float(0xFFFFFFFFu); -}
\ No newline at end of file +} diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl index 092ae45b719..faae68d2f55 100644 --- a/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl +++ b/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl @@ -30,4 +30,4 @@ void main() texCoord_interp = tex.zy; } gl_Position = vec4(rect.xy, 0.0f, 1.0f); -}
\ No newline at end of file +} diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh index aa198482291..1849a04ea48 100644 --- a/source/blender/gpu/metal/mtl_context.hh +++ b/source/blender/gpu/metal/mtl_context.hh @@ -7,8 +7,10 @@ #include "gpu_context_private.hh" +#include "GPU_common_types.h" #include "GPU_context.h" +#include "mtl_capabilities.hh" #include "mtl_texture.hh" #include <Cocoa/Cocoa.h> @@ -21,6 +23,112 @@ namespace blender::gpu { +class MTLShader; +class MTLUniformBuf; +class MTLBuffer; + +/* Depth Stencil State */ +typedef struct MTLContextDepthStencilState { + + /* Depth State. */ + bool depth_write_enable; + bool depth_test_enabled; + float depth_range_near; + float depth_range_far; + MTLCompareFunction depth_function; + float depth_bias; + float depth_slope_scale; + bool depth_bias_enabled_for_points; + bool depth_bias_enabled_for_lines; + bool depth_bias_enabled_for_tris; + + /* Stencil State. */ + bool stencil_test_enabled; + unsigned int stencil_read_mask; + unsigned int stencil_write_mask; + unsigned int stencil_ref; + MTLCompareFunction stencil_func; + + MTLStencilOperation stencil_op_front_stencil_fail; + MTLStencilOperation stencil_op_front_depth_fail; + MTLStencilOperation stencil_op_front_depthstencil_pass; + + MTLStencilOperation stencil_op_back_stencil_fail; + MTLStencilOperation stencil_op_back_depth_fail; + MTLStencilOperation stencil_op_back_depthstencil_pass; + + /* Frame-buffer State -- We need to mark this, in case stencil state remains unchanged, + * but attachment state has changed. */ + bool has_depth_target; + bool has_stencil_target; + + /* TODO(Metal): Consider optimizing this function using memcmp. + * Un-used, but differing, stencil state leads to over-generation + * of state objects when doing trivial compare. */ + inline bool operator==(const MTLContextDepthStencilState &other) const + { + bool depth_state_equality = (has_depth_target == other.has_depth_target && + depth_write_enable == other.depth_write_enable && + depth_test_enabled == other.depth_test_enabled && + depth_function == other.depth_function); + + bool stencil_state_equality = true; + if (has_stencil_target) { + stencil_state_equality = + (has_stencil_target == other.has_stencil_target && + stencil_test_enabled == other.stencil_test_enabled && + stencil_op_front_stencil_fail == other.stencil_op_front_stencil_fail && + stencil_op_front_depth_fail == other.stencil_op_front_depth_fail && + stencil_op_front_depthstencil_pass == other.stencil_op_front_depthstencil_pass && + stencil_op_back_stencil_fail == other.stencil_op_back_stencil_fail && + stencil_op_back_depth_fail == other.stencil_op_back_depth_fail && + stencil_op_back_depthstencil_pass == other.stencil_op_back_depthstencil_pass && + stencil_func == other.stencil_func && stencil_read_mask == other.stencil_read_mask && + stencil_write_mask == other.stencil_write_mask); + } + + return depth_state_equality && stencil_state_equality; + } + + /* Depth stencil state will get hashed in order to prepare + * MTLDepthStencilState objects. The hash should comprise of + * all elements which fill the MTLDepthStencilDescriptor. + * These are bound when [rec setDepthStencilState:...] is called. + * Depth bias and stencil reference value are set dynamically on the RenderCommandEncoder: + * - setStencilReferenceValue: + * - setDepthBias:slopeScale:clamp: + */ + inline std::size_t hash() const + { + std::size_t boolean_bitmask = (this->depth_write_enable ? 1 : 0) | + ((this->depth_test_enabled ? 1 : 0) << 1) | + ((this->depth_bias_enabled_for_points ? 1 : 0) << 2) | + ((this->depth_bias_enabled_for_lines ? 1 : 0) << 3) | + ((this->depth_bias_enabled_for_tris ? 1 : 0) << 4) | + ((this->stencil_test_enabled ? 1 : 0) << 5) | + ((this->has_depth_target ? 1 : 0) << 6) | + ((this->has_stencil_target ? 1 : 0) << 7); + + std::size_t stencilop_bitmask = ((std::size_t)this->stencil_op_front_stencil_fail) | + ((std::size_t)this->stencil_op_front_depth_fail << 3) | + ((std::size_t)this->stencil_op_front_depthstencil_pass << 6) | + ((std::size_t)this->stencil_op_back_stencil_fail << 9) | + ((std::size_t)this->stencil_op_back_depth_fail << 12) | + ((std::size_t)this->stencil_op_back_depthstencil_pass << 15); + + std::size_t main_hash = (std::size_t)this->depth_function; + if (this->has_stencil_target) { + main_hash += (std::size_t)(this->stencil_read_mask & 0xFF) << 8; + main_hash += (std::size_t)(this->stencil_write_mask & 0xFF) << 16; + } + main_hash ^= (std::size_t)this->stencil_func << 16; + main_hash ^= stencilop_bitmask; + + std::size_t final_hash = (main_hash << 8) | boolean_bitmask; + return final_hash; + } +} MTLContextDepthStencilState; + typedef struct MTLContextTextureUtils { /* Depth Update Utilities */ @@ -108,11 +216,149 @@ typedef struct MTLContextTextureUtils { } MTLContextTextureUtils; +/* Structs containing information on current binding state for textures and samplers. */ +typedef struct MTLTextureBinding { + bool used; + + /* Same value as index in bindings array. */ + unsigned int texture_slot_index; + gpu::MTLTexture *texture_resource; + +} MTLTextureBinding; + +typedef struct MTLSamplerBinding { + bool used; + MTLSamplerState state; + + bool operator==(MTLSamplerBinding const &other) const + { + return (used == other.used && state == other.state); + } +} MTLSamplerBinding; + +/* Combined sampler state configuration for Argument Buffer caching. */ +struct MTLSamplerArray { + unsigned int num_samplers; + /* MTLSamplerState permutations between 0..256 - slightly more than a byte. */ + MTLSamplerState mtl_sampler_flags[MTL_MAX_TEXTURE_SLOTS]; + id<MTLSamplerState> mtl_sampler[MTL_MAX_TEXTURE_SLOTS]; + + inline bool operator==(const MTLSamplerArray &other) const + { + if (this->num_samplers != other.num_samplers) { + return false; + } + return (memcmp(this->mtl_sampler_flags, + other.mtl_sampler_flags, + sizeof(MTLSamplerState) * this->num_samplers) == 0); + } + + inline uint32_t hash() const + { + uint32_t hash = this->num_samplers; + for (int i = 0; i < this->num_samplers; i++) { + hash ^= (uint32_t)this->mtl_sampler_flags[i] << (i % 3); + } + return hash; + } +}; + +typedef enum MTLPipelineStateDirtyFlag { + MTL_PIPELINE_STATE_NULL_FLAG = 0, + /* Whether we need to call setViewport. */ + MTL_PIPELINE_STATE_VIEWPORT_FLAG = (1 << 0), + /* Whether we need to call setScissor.*/ + MTL_PIPELINE_STATE_SCISSOR_FLAG = (1 << 1), + /* Whether we need to update/rebind active depth stencil state. */ + MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG = (1 << 2), + /* Whether we need to update/rebind active PSO. */ + MTL_PIPELINE_STATE_PSO_FLAG = (1 << 3), + /* Whether we need to update the frontFacingWinding state. */ + MTL_PIPELINE_STATE_FRONT_FACING_FLAG = (1 << 4), + /* Whether we need to update the culling state. */ + MTL_PIPELINE_STATE_CULLMODE_FLAG = (1 << 5), + /* Full pipeline state needs applying. Occurs when beginning a new render pass. */ + MTL_PIPELINE_STATE_ALL_FLAG = + (MTL_PIPELINE_STATE_VIEWPORT_FLAG | MTL_PIPELINE_STATE_SCISSOR_FLAG | + MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG | MTL_PIPELINE_STATE_PSO_FLAG | + MTL_PIPELINE_STATE_FRONT_FACING_FLAG | MTL_PIPELINE_STATE_CULLMODE_FLAG) +} MTLPipelineStateDirtyFlag; + +/* Ignore full flag bit-mask `MTL_PIPELINE_STATE_ALL_FLAG`. */ +ENUM_OPERATORS(MTLPipelineStateDirtyFlag, MTL_PIPELINE_STATE_CULLMODE_FLAG); + +typedef struct MTLUniformBufferBinding { + bool bound; + MTLUniformBuf *ubo; +} MTLUniformBufferBinding; + typedef struct MTLContextGlobalShaderPipelineState { - /* ..TODO(Metal): More elements to be added as backend fleshed out.. */ + bool initialised; + + /* Whether the pipeline state has been modified since application. + * `dirty_flags` is a bitmask of the types of state which have been updated. + * This is in order to optimize calls and only re-apply state as needed. + * Some state parameters are dynamically applied on the RenderCommandEncoder, + * others may be encapsulated in GPU-resident state objects such as + * MTLDepthStencilState or MTLRenderPipelineState. */ + bool dirty; + MTLPipelineStateDirtyFlag dirty_flags; + + /* Shader resources. */ + MTLShader *null_shader; + + /* Active Shader State. */ + MTLShader *active_shader; + + /* Global Uniform Buffers. */ + MTLUniformBufferBinding ubo_bindings[MTL_MAX_UNIFORM_BUFFER_BINDINGS]; + + /* Context Texture bindings. */ + MTLTextureBinding texture_bindings[MTL_MAX_TEXTURE_SLOTS]; + MTLSamplerBinding sampler_bindings[MTL_MAX_SAMPLER_SLOTS]; + + /*** --- Render Pipeline State --- ***/ + /* Track global render pipeline state for the current context. The functions in GPU_state.h + * modify these parameters. Certain values, tagged [PSO], are parameters which are required to be + * passed into PSO creation, rather than dynamic state functions on the RenderCommandEncoder. + */ - /*** DATA and IMAGE access state ***/ + /* Blending State. */ + MTLColorWriteMask color_write_mask; /* [PSO] */ + bool blending_enabled; /* [PSO] */ + MTLBlendOperation alpha_blend_op; /* [PSO] */ + MTLBlendOperation rgb_blend_op; /* [PSO] */ + MTLBlendFactor dest_alpha_blend_factor; /* [PSO] */ + MTLBlendFactor dest_rgb_blend_factor; /* [PSO] */ + MTLBlendFactor src_alpha_blend_factor; /* [PSO] */ + MTLBlendFactor src_rgb_blend_factor; /* [PSO] */ + + /* Culling State. */ + bool culling_enabled; + eGPUFaceCullTest cull_mode; + eGPUFrontFace front_face; + + /* Depth State. */ + MTLContextDepthStencilState depth_stencil_state; + + /* Viewport/Scissor Region. */ + int viewport_offset_x; + int viewport_offset_y; + int viewport_width; + int viewport_height; + bool scissor_enabled; + int scissor_x; + int scissor_y; + int scissor_width; + int scissor_height; + + /* Image data access state. */ uint unpack_row_length; + + /* Render parameters. */ + float point_size = 1.0f; + float line_width = 1.0f; + } MTLContextGlobalShaderPipelineState; /* Metal Buffer */ @@ -127,8 +373,8 @@ typedef struct MTLTemporaryBufferRange { bool requires_flush(); } MTLTemporaryBufferRange; -/** MTLContext -- Core render loop and state management **/ -/* Note(Metal): Partial MTLContext stub to provide wrapper functionality +/** MTLContext -- Core render loop and state management. **/ +/* NOTE(Metal): Partial MTLContext stub to provide wrapper functionality * for work-in-progress MTL* classes. */ class MTLContext : public Context { @@ -138,8 +384,24 @@ class MTLContext : public Context { /* Compute and specialization caches. */ MTLContextTextureUtils texture_utils_; + /* Texture Samplers. */ + /* Cache of generated MTLSamplerState objects based on permutations of `eGPUSamplerState`. */ + id<MTLSamplerState> sampler_state_cache_[GPU_SAMPLER_MAX] = {0}; + id<MTLSamplerState> default_sampler_state_ = nil; + + /* When texture sampler count exceeds the resource bind limit, an + * argument buffer is used to pass samplers to the shader. + * Each unique configurations of multiple samplers can be cached, so as to not require + * re-generation. `samplers_` stores the current list of bound sampler objects. + * `cached_sampler_buffers_` is a cache of encoded argument buffers which can be re-used. */ + MTLSamplerArray samplers_; + blender::Map<MTLSamplerArray, gpu::MTLBuffer *> cached_sampler_buffers_; + public: - /* METAL API Resource Handles. */ + /* Shaders and Pipeline state. */ + MTLContextGlobalShaderPipelineState pipeline_state; + + /* Metal API Resource Handles. */ id<MTLCommandQueue> queue = nil; id<MTLDevice> device = nil; @@ -160,24 +422,40 @@ class MTLContext : public Context { void debug_group_begin(const char *name, int index) override; void debug_group_end(void) override; - /*** Context Utility functions */ + /*** MTLContext Utility functions. */ /* * All below functions modify the global state for the context, controlling the flow of * rendering, binding resources, setting global state, resource management etc; */ - /* Metal Context Core functions */ - /* Command Buffer Management */ + /* Metal Context Core functions. */ + /* Command Buffer Management. */ id<MTLCommandBuffer> get_active_command_buffer(); - /* Render Pass State and Management */ + /* Render Pass State and Management. */ void begin_render_pass(); void end_render_pass(); - - /* Shaders and Pipeline state */ - MTLContextGlobalShaderPipelineState pipeline_state; - - /* Texture utilities */ + bool is_render_pass_active(); + + /* Texture Binding. */ + void texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit); + void sampler_bind(MTLSamplerState, unsigned int sampler_unit); + void texture_unbind(gpu::MTLTexture *mtl_texture); + void texture_unbind_all(void); + id<MTLSamplerState> get_sampler_from_state(MTLSamplerState state); + id<MTLSamplerState> generate_sampler_from_state(MTLSamplerState state); + id<MTLSamplerState> get_default_sampler_state(); + + /* Metal Context pipeline state. */ + void pipeline_state_init(void); + MTLShader *get_active_shader(void); + + /* State assignment. */ + void set_viewport(int origin_x, int origin_y, int width, int height); + void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height); + void set_scissor_enabled(bool scissor_enabled); + + /* Texture utilities. */ MTLContextTextureUtils &get_texture_utils() { return this->texture_utils_; diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm index 18ed38c373d..94f5682b11b 100644 --- a/source/blender/gpu/metal/mtl_context.mm +++ b/source/blender/gpu/metal/mtl_context.mm @@ -5,6 +5,11 @@ */ #include "mtl_context.hh" #include "mtl_debug.hh" +#include "mtl_state.hh" + +#include "DNA_userdef_types.h" + +#include "GPU_capabilities.h" using namespace blender; using namespace blender::gpu; @@ -44,6 +49,9 @@ MTLContext::MTLContext(void *ghost_window) /* Init debug. */ debug::mtl_debug_init(); + /* Initialize Metal modules. */ + this->state_manager = new MTLStateManager(this); + /* TODO(Metal): Implement. */ } @@ -98,6 +106,234 @@ void MTLContext::end_render_pass() /* TODO(Metal): Implement. */ } +bool MTLContext::is_render_pass_active() +{ + /* TODO(Metal): Implement. */ + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Global Context State + * \{ */ + +/* Metal Context Pipeline State. */ +void MTLContext::pipeline_state_init() +{ + /*** Initialize state only once. ***/ + if (!this->pipeline_state.initialised) { + this->pipeline_state.initialised = true; + this->pipeline_state.active_shader = NULL; + + /* Clear bindings state. */ + for (int t = 0; t < GPU_max_textures(); t++) { + this->pipeline_state.texture_bindings[t].used = false; + this->pipeline_state.texture_bindings[t].texture_slot_index = t; + this->pipeline_state.texture_bindings[t].texture_resource = NULL; + } + for (int s = 0; s < MTL_MAX_SAMPLER_SLOTS; s++) { + this->pipeline_state.sampler_bindings[s].used = false; + } + for (int u = 0; u < MTL_MAX_UNIFORM_BUFFER_BINDINGS; u++) { + this->pipeline_state.ubo_bindings[u].bound = false; + this->pipeline_state.ubo_bindings[u].ubo = NULL; + } + } + + /*** State defaults -- restored by GPU_state_init. ***/ + /* Clear blending State. */ + this->pipeline_state.color_write_mask = MTLColorWriteMaskRed | MTLColorWriteMaskGreen | + MTLColorWriteMaskBlue | MTLColorWriteMaskAlpha; + this->pipeline_state.blending_enabled = false; + this->pipeline_state.alpha_blend_op = MTLBlendOperationAdd; + this->pipeline_state.rgb_blend_op = MTLBlendOperationAdd; + this->pipeline_state.dest_alpha_blend_factor = MTLBlendFactorZero; + this->pipeline_state.dest_rgb_blend_factor = MTLBlendFactorZero; + this->pipeline_state.src_alpha_blend_factor = MTLBlendFactorOne; + this->pipeline_state.src_rgb_blend_factor = MTLBlendFactorOne; + + /* Viewport and scissor. */ + this->pipeline_state.viewport_offset_x = 0; + this->pipeline_state.viewport_offset_y = 0; + this->pipeline_state.viewport_width = 0; + this->pipeline_state.viewport_height = 0; + this->pipeline_state.scissor_x = 0; + this->pipeline_state.scissor_y = 0; + this->pipeline_state.scissor_width = 0; + this->pipeline_state.scissor_height = 0; + this->pipeline_state.scissor_enabled = false; + + /* Culling State. */ + this->pipeline_state.culling_enabled = false; + this->pipeline_state.cull_mode = GPU_CULL_NONE; + this->pipeline_state.front_face = GPU_COUNTERCLOCKWISE; + + /* DATA and IMAGE access state. */ + this->pipeline_state.unpack_row_length = 0; + + /* Depth State. */ + this->pipeline_state.depth_stencil_state.depth_write_enable = false; + this->pipeline_state.depth_stencil_state.depth_test_enabled = false; + this->pipeline_state.depth_stencil_state.depth_range_near = 0.0; + this->pipeline_state.depth_stencil_state.depth_range_far = 1.0; + this->pipeline_state.depth_stencil_state.depth_function = MTLCompareFunctionAlways; + this->pipeline_state.depth_stencil_state.depth_bias = 0.0; + this->pipeline_state.depth_stencil_state.depth_slope_scale = 0.0; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_points = false; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_lines = false; + this->pipeline_state.depth_stencil_state.depth_bias_enabled_for_tris = false; + + /* Stencil State. */ + this->pipeline_state.depth_stencil_state.stencil_test_enabled = false; + this->pipeline_state.depth_stencil_state.stencil_read_mask = 0xFF; + this->pipeline_state.depth_stencil_state.stencil_write_mask = 0xFF; + this->pipeline_state.depth_stencil_state.stencil_ref = 0; + this->pipeline_state.depth_stencil_state.stencil_func = MTLCompareFunctionAlways; + this->pipeline_state.depth_stencil_state.stencil_op_front_stencil_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_front_depth_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_front_depthstencil_pass = + MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_stencil_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_depth_fail = MTLStencilOperationKeep; + this->pipeline_state.depth_stencil_state.stencil_op_back_depthstencil_pass = + MTLStencilOperationKeep; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture State Management + * \{ */ + +void MTLContext::texture_bind(gpu::MTLTexture *mtl_texture, unsigned int texture_unit) +{ + BLI_assert(this); + BLI_assert(mtl_texture); + + if (texture_unit < 0 || texture_unit >= GPU_max_textures() || + texture_unit >= MTL_MAX_TEXTURE_SLOTS) { + MTL_LOG_WARNING("Attempting to bind texture '%s' to invalid texture unit %d\n", + mtl_texture->get_name(), + texture_unit); + BLI_assert(false); + return; + } + + /* Bind new texture. */ + this->pipeline_state.texture_bindings[texture_unit].texture_resource = mtl_texture; + this->pipeline_state.texture_bindings[texture_unit].used = true; + mtl_texture->is_bound_ = true; +} + +void MTLContext::sampler_bind(MTLSamplerState sampler_state, unsigned int sampler_unit) +{ + BLI_assert(this); + if (sampler_unit < 0 || sampler_unit >= GPU_max_textures() || + sampler_unit >= MTL_MAX_SAMPLER_SLOTS) { + MTL_LOG_WARNING("Attempting to bind sampler to invalid sampler unit %d\n", sampler_unit); + BLI_assert(false); + return; + } + + /* Apply binding. */ + this->pipeline_state.sampler_bindings[sampler_unit] = {true, sampler_state}; +} + +void MTLContext::texture_unbind(gpu::MTLTexture *mtl_texture) +{ + BLI_assert(mtl_texture); + + /* Iterate through textures in state and unbind. */ + for (int i = 0; i < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); i++) { + if (this->pipeline_state.texture_bindings[i].texture_resource == mtl_texture) { + this->pipeline_state.texture_bindings[i].texture_resource = nullptr; + this->pipeline_state.texture_bindings[i].used = false; + } + } + + /* Locally unbind texture. */ + mtl_texture->is_bound_ = false; +} + +void MTLContext::texture_unbind_all() +{ + /* Iterate through context's bound textures. */ + for (int t = 0; t < min_uu(GPU_max_textures(), MTL_MAX_TEXTURE_SLOTS); t++) { + if (this->pipeline_state.texture_bindings[t].used && + this->pipeline_state.texture_bindings[t].texture_resource) { + + this->pipeline_state.texture_bindings[t].used = false; + this->pipeline_state.texture_bindings[t].texture_resource = nullptr; + } + } +} + +id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_state) +{ + BLI_assert((unsigned int)sampler_state >= 0 && ((unsigned int)sampler_state) < GPU_SAMPLER_MAX); + return this->sampler_state_cache_[(unsigned int)sampler_state]; +} + +id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState sampler_state) +{ + /* Check if sampler already exists for given state. */ + id<MTLSamplerState> st = this->sampler_state_cache_[(unsigned int)sampler_state]; + if (st != nil) { + return st; + } + else { + MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init]; + descriptor.normalizedCoordinates = true; + + MTLSamplerAddressMode clamp_type = (sampler_state.state & GPU_SAMPLER_CLAMP_BORDER) ? + MTLSamplerAddressModeClampToBorderColor : + MTLSamplerAddressModeClampToEdge; + descriptor.rAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_R) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.sAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_S) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.tAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_T) ? + MTLSamplerAddressModeRepeat : + clamp_type; + descriptor.borderColor = MTLSamplerBorderColorTransparentBlack; + descriptor.minFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ? + MTLSamplerMinMagFilterLinear : + MTLSamplerMinMagFilterNearest; + descriptor.magFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ? + MTLSamplerMinMagFilterLinear : + MTLSamplerMinMagFilterNearest; + descriptor.mipFilter = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? + MTLSamplerMipFilterLinear : + MTLSamplerMipFilterNotMipmapped; + descriptor.lodMinClamp = -1000; + descriptor.lodMaxClamp = 1000; + float aniso_filter = max_ff(16, U.anisotropic_filter); + descriptor.maxAnisotropy = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? aniso_filter : 1; + descriptor.compareFunction = (sampler_state.state & GPU_SAMPLER_COMPARE) ? + MTLCompareFunctionLessEqual : + MTLCompareFunctionAlways; + descriptor.supportArgumentBuffers = true; + + id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor]; + this->sampler_state_cache_[(unsigned int)sampler_state] = state; + + BLI_assert(state != nil); + [descriptor autorelease]; + return state; + } +} + +id<MTLSamplerState> MTLContext::get_default_sampler_state() +{ + if (this->default_sampler_state_ == nil) { + this->default_sampler_state_ = this->get_sampler_from_state(DEFAULT_SAMPLER_STATE); + } + return this->default_sampler_state_; +} + /** \} */ } // blender::gpu diff --git a/source/blender/gpu/metal/mtl_state.hh b/source/blender/gpu/metal/mtl_state.hh new file mode 100644 index 00000000000..f2d85f9648b --- /dev/null +++ b/source/blender/gpu/metal/mtl_state.hh @@ -0,0 +1,73 @@ +/** \file + * \ingroup gpu + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "GPU_state.h" +#include "gpu_state_private.hh" + +namespace blender::gpu { + +/* Forward Declarations. */ +class MTLContext; + +/** + * State manager keeping track of the draw state and applying it before drawing. + * Metal Implementation. + **/ +class MTLStateManager : public StateManager { + public: + private: + /* Current state of the associated MTLContext. + * Avoids resetting the whole state for every change. */ + GPUState current_; + GPUStateMutable current_mutable_; + MTLContext *context_; + + public: + MTLStateManager(MTLContext *ctx); + + void apply_state(void) override; + void force_state(void) override; + + void issue_barrier(eGPUBarrier barrier_bits) override; + + void texture_bind(Texture *tex, eGPUSamplerState sampler, int unit) override; + void texture_unbind(Texture *tex) override; + void texture_unbind_all(void) override; + + void image_bind(Texture *tex, int unit) override; + void image_unbind(Texture *tex) override; + void image_unbind_all(void) override; + + void texture_unpack_row_length_set(uint len) override; + + private: + void set_write_mask(const eGPUWriteMask value); + void set_depth_test(const eGPUDepthTest value); + void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation); + void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state); + void set_clip_distances(const int new_dist_len, const int old_dist_len); + void set_logic_op(const bool enable); + void set_facing(const bool invert); + void set_backface_culling(const eGPUFaceCullTest test); + void set_provoking_vert(const eGPUProvokingVertex vert); + void set_shadow_bias(const bool enable); + void set_blend(const eGPUBlend value); + + void set_state(const GPUState &state); + void set_mutable_state(const GPUStateMutable &state); + + /* METAL State utility functions. */ + void mtl_state_init(void); + void mtl_depth_range(float near, float far); + void mtl_stencil_mask(unsigned int mask); + void mtl_stencil_set_func(eGPUStencilTest stencil_func, int ref, unsigned int mask); + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLStateManager") +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_state.mm b/source/blender/gpu/metal/mtl_state.mm new file mode 100644 index 00000000000..5f52bc55f72 --- /dev/null +++ b/source/blender/gpu/metal/mtl_state.mm @@ -0,0 +1,675 @@ +/** \file + * \ingroup gpu + */ + +#include "BLI_math_base.h" +#include "BLI_math_bits.h" + +#include "GPU_framebuffer.h" + +#include "mtl_context.hh" +#include "mtl_state.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name MTLStateManager + * \{ */ + +void MTLStateManager::mtl_state_init(void) +{ + BLI_assert(this->context_); + this->context_->pipeline_state_init(); +} + +MTLStateManager::MTLStateManager(MTLContext *ctx) : StateManager() +{ + /* Initialize State. */ + this->context_ = ctx; + mtl_state_init(); + + /* Force update using default state. */ + current_ = ~state; + current_mutable_ = ~mutable_state; + set_state(state); + set_mutable_state(mutable_state); +} + +void MTLStateManager::apply_state(void) +{ + this->set_state(this->state); + this->set_mutable_state(this->mutable_state); + /* TODO(Metal): Enable after integration of MTLFrameBuffer. */ + /* static_cast<MTLFrameBuffer *>(this->context_->active_fb)->apply_state(); */ +}; + +void MTLStateManager::force_state(void) +{ + /* 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 MTLStateManager::set_state(const GPUState &state) +{ + GPUState changed = state ^ current_; + + if (changed.blend != 0) { + set_blend((eGPUBlend)state.blend); + } + if (changed.write_mask != 0) { + set_write_mask((eGPUWriteMask)state.write_mask); + } + if (changed.depth_test != 0) { + set_depth_test((eGPUDepthTest)state.depth_test); + } + if (changed.stencil_test != 0 || changed.stencil_op != 0) { + set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op); + set_stencil_mask((eGPUStencilTest)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((eGPUFaceCullTest)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((eGPUProvokingVertex)state.provoking_vert); + } + if (changed.shadow_bias != 0) { + set_shadow_bias(state.shadow_bias); + } + + /* TODO remove (Following GLState). */ + if (changed.polygon_smooth) { + /* NOTE: Unsupported in Metal. */ + } + if (changed.line_smooth) { + /* NOTE: Unsupported in Metal. */ + } + + current_ = state; +} + +void MTLStateManager::mtl_depth_range(float near, float far) +{ + BLI_assert(this->context_); + BLI_assert(near >= 0.0 && near < 1.0); + BLI_assert(far > 0.0 && far <= 1.0); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.depth_range_near = near; + ds_state.depth_range_far = far; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_VIEWPORT_FLAG; +} + +void MTLStateManager::set_mutable_state(const GPUStateMutable &state) +{ + GPUStateMutable changed = state ^ current_mutable_; + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + if (float_as_uint(changed.point_size) != 0) { + pipeline_state.point_size = state.point_size; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + } + + if (changed.line_width != 0) { + pipeline_state.line_width = state.line_width; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + } + + if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) { + /* TODO remove, should modify the projection matrix instead. */ + mtl_depth_range(state.depth_range[0], state.depth_range[1]); + } + + if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 || + changed.stencil_write_mask != 0) { + set_stencil_mask((eGPUStencilTest)current_.stencil_test, state); + } + + current_mutable_ = state; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State setting functions + * \{ */ + +void MTLStateManager::set_write_mask(const eGPUWriteMask value) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.depth_write_enable = ((value & GPU_WRITE_DEPTH) != 0); + pipeline_state.color_write_mask = + (((value & GPU_WRITE_RED) != 0) ? MTLColorWriteMaskRed : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_GREEN) != 0) ? MTLColorWriteMaskGreen : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_BLUE) != 0) ? MTLColorWriteMaskBlue : MTLColorWriteMaskNone) | + (((value & GPU_WRITE_ALPHA) != 0) ? MTLColorWriteMaskAlpha : MTLColorWriteMaskNone); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; +} + +static MTLCompareFunction gpu_depth_function_to_metal(eGPUDepthTest depth_func) +{ + switch (depth_func) { + case GPU_DEPTH_NONE: + return MTLCompareFunctionNever; + case GPU_DEPTH_LESS: + return MTLCompareFunctionLess; + case GPU_DEPTH_EQUAL: + return MTLCompareFunctionEqual; + case GPU_DEPTH_LESS_EQUAL: + return MTLCompareFunctionLessEqual; + case GPU_DEPTH_GREATER: + return MTLCompareFunctionGreater; + case GPU_DEPTH_GREATER_EQUAL: + return MTLCompareFunctionGreaterEqual; + case GPU_DEPTH_ALWAYS: + return MTLCompareFunctionAlways; + default: + BLI_assert(false && "Invalid eGPUDepthTest"); + break; + } + return MTLCompareFunctionAlways; +} + +static MTLCompareFunction gpu_stencil_func_to_metal(eGPUStencilTest stencil_func) +{ + switch (stencil_func) { + case GPU_STENCIL_NONE: + return MTLCompareFunctionAlways; + case GPU_STENCIL_EQUAL: + return MTLCompareFunctionEqual; + case GPU_STENCIL_NEQUAL: + return MTLCompareFunctionNotEqual; + case GPU_STENCIL_ALWAYS: + return MTLCompareFunctionAlways; + default: + BLI_assert(false && "Unrecognised eGPUStencilTest function"); + break; + } + return MTLCompareFunctionAlways; +} + +void MTLStateManager::set_depth_test(const eGPUDepthTest value) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.depth_test_enabled = (value != GPU_DEPTH_NONE); + ds_state.depth_function = gpu_depth_function_to_metal(value); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::mtl_stencil_mask(unsigned int mask) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.stencil_write_mask = mask; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::mtl_stencil_set_func(eGPUStencilTest stencil_func, + int ref, + unsigned int mask) +{ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + ds_state.stencil_func = gpu_stencil_func_to_metal(stencil_func); + ds_state.stencil_ref = ref; + ds_state.stencil_read_mask = mask; + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +static void mtl_stencil_set_op_separate(MTLContext *context, + eGPUFaceCullTest face, + MTLStencilOperation stencil_fail, + MTLStencilOperation depth_test_fail, + MTLStencilOperation depthstencil_pass) +{ + BLI_assert(context); + MTLContextGlobalShaderPipelineState &pipeline_state = context->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + if (face == GPU_CULL_FRONT) { + ds_state.stencil_op_front_stencil_fail = stencil_fail; + ds_state.stencil_op_front_depth_fail = depth_test_fail; + ds_state.stencil_op_front_depthstencil_pass = depthstencil_pass; + } + else if (face == GPU_CULL_BACK) { + ds_state.stencil_op_back_stencil_fail = stencil_fail; + ds_state.stencil_op_back_depth_fail = depth_test_fail; + ds_state.stencil_op_back_depthstencil_pass = depthstencil_pass; + } + + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +static void mtl_stencil_set_op(MTLContext *context, + MTLStencilOperation stencil_fail, + MTLStencilOperation depth_test_fail, + MTLStencilOperation depthstencil_pass) +{ + mtl_stencil_set_op_separate( + context, GPU_CULL_FRONT, stencil_fail, depth_test_fail, depthstencil_pass); + mtl_stencil_set_op_separate( + context, GPU_CULL_BACK, stencil_fail, depth_test_fail, depthstencil_pass); +} + +void MTLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation) +{ + switch (operation) { + case GPU_STENCIL_OP_REPLACE: + mtl_stencil_set_op(this->context_, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationReplace); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_PASS: + /* Winding inversed due to flipped Y coordinate system in Metal. */ + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_FRONT, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationIncrementWrap); + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_BACK, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationDecrementWrap); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_FAIL: + /* Winding inversed due to flipped Y coordinate system in Metal. */ + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_FRONT, + MTLStencilOperationKeep, + MTLStencilOperationDecrementWrap, + MTLStencilOperationKeep); + mtl_stencil_set_op_separate(this->context_, + GPU_CULL_BACK, + MTLStencilOperationKeep, + MTLStencilOperationIncrementWrap, + MTLStencilOperationKeep); + break; + case GPU_STENCIL_OP_NONE: + default: + mtl_stencil_set_op(this->context_, + MTLStencilOperationKeep, + MTLStencilOperationKeep, + MTLStencilOperationKeep); + } + + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + pipeline_state.depth_stencil_state.stencil_test_enabled = (test != GPU_STENCIL_NONE); + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; +} + +void MTLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state) +{ + if (test == GPU_STENCIL_NONE) { + mtl_stencil_mask(0x00); + mtl_stencil_set_func(GPU_STENCIL_ALWAYS, 0x00, 0x00); + } + else { + mtl_stencil_mask(state.stencil_write_mask); + mtl_stencil_set_func(test, state.stencil_reference, state.stencil_compare_mask); + } +} + +void MTLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len) +{ + /* TODO(Metal): Support Clip distances in METAL. Clip distance + * assignment via shader is supported, but global clip-states require + * support. */ +} + +void MTLStateManager::set_logic_op(const bool enable) +{ + /* NOTE(Metal): Logic Operations not directly supported. */ +} + +void MTLStateManager::set_facing(const bool invert) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + /* Apply State -- opposite of GL, as METAL default is GPU_CLOCKWISE, GL default is + * COUNTERCLOCKWISE. This needs to be the inverse of the default. */ + pipeline_state.front_face = (invert) ? GPU_COUNTERCLOCKWISE : GPU_CLOCKWISE; + + /* Mark Dirty - Ensure context updates state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_FRONT_FACING_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_backface_culling(const eGPUFaceCullTest test) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + /* Apply State. */ + pipeline_state.culling_enabled = (test != GPU_CULL_NONE); + pipeline_state.cull_mode = test; + + /* Mark Dirty - Ensure context updates state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_CULLMODE_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_provoking_vert(const eGPUProvokingVertex vert) +{ + /* NOTE(Metal): Provoking vertex is not a feature in the Metal API. + * Shaders are handled on a case-by-case basis using a modified vertex shader. + * For example, wireframe rendering and edit-mesh shaders utilize an SSBO-based + * vertex fetching mechanism which considers the inverse convention for flat + * shading, to ensure consistent results with OpenGL. */ +} + +void MTLStateManager::set_shadow_bias(const bool enable) +{ + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + MTLContextDepthStencilState &ds_state = pipeline_state.depth_stencil_state; + + /* Apply State. */ + if (enable) { + ds_state.depth_bias_enabled_for_lines = true; + ds_state.depth_bias_enabled_for_tris = true; + ds_state.depth_bias = 2.0f; + ds_state.depth_slope_scale = 1.0f; + } + else { + ds_state.depth_bias_enabled_for_lines = false; + ds_state.depth_bias_enabled_for_tris = false; + ds_state.depth_bias = 0.0f; + ds_state.depth_slope_scale = 0.0f; + } + + /* Mark Dirty - Ensure context updates depth-stencil state between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG; + pipeline_state.dirty = true; +} + +void MTLStateManager::set_blend(const eGPUBlend value) +{ + /** + * Factors to the equation. + * SRC is fragment shader output. + * DST is framebuffer color. + * final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb; + * final.a = SRC.a * src_alpha + DST.a * dst_alpha; + **/ + MTLBlendFactor src_rgb; + MTLBlendFactor dst_rgb; + MTLBlendFactor src_alpha; + MTLBlendFactor dst_alpha; + switch (value) { + default: + case GPU_BLEND_ALPHA: { + src_rgb = MTLBlendFactorSourceAlpha; + dst_rgb = MTLBlendFactorOneMinusSourceAlpha; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_ALPHA_PREMULT: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOneMinusSourceAlpha; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_ADDITIVE: { + /* Do not let alpha accumulate but pre-multiply the source RGB by it. */ + src_rgb = MTLBlendFactorSourceAlpha; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_SUBTRACT: + case GPU_BLEND_ADDITIVE_PREMULT: { + /* Let alpha accumulate. */ + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_MULTIPLY: { + src_rgb = MTLBlendFactorDestinationColor; + dst_rgb = MTLBlendFactorZero; + src_alpha = MTLBlendFactorDestinationAlpha; + dst_alpha = MTLBlendFactorZero; + break; + } + case GPU_BLEND_INVERT: { + src_rgb = MTLBlendFactorOneMinusDestinationColor; + dst_rgb = MTLBlendFactorZero; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_OIT: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorOneMinusSourceAlpha; + break; + } + case GPU_BLEND_BACKGROUND: { + src_rgb = MTLBlendFactorOneMinusDestinationAlpha; + dst_rgb = MTLBlendFactorSourceAlpha; + src_alpha = MTLBlendFactorZero; + dst_alpha = MTLBlendFactorSourceAlpha; + break; + } + case GPU_BLEND_ALPHA_UNDER_PREMUL: { + src_rgb = MTLBlendFactorOneMinusDestinationAlpha; + dst_rgb = MTLBlendFactorOne; + src_alpha = MTLBlendFactorOneMinusDestinationAlpha; + dst_alpha = MTLBlendFactorOne; + break; + } + case GPU_BLEND_CUSTOM: { + src_rgb = MTLBlendFactorOne; + dst_rgb = MTLBlendFactorSource1Color; + src_alpha = MTLBlendFactorOne; + dst_alpha = MTLBlendFactorSource1Alpha; + break; + } + } + + /* Check Current Context. */ + BLI_assert(this->context_); + MTLContextGlobalShaderPipelineState &pipeline_state = this->context_->pipeline_state; + + if (value == GPU_BLEND_SUBTRACT) { + pipeline_state.rgb_blend_op = MTLBlendOperationReverseSubtract; + pipeline_state.alpha_blend_op = MTLBlendOperationReverseSubtract; + } + else { + pipeline_state.rgb_blend_op = MTLBlendOperationAdd; + pipeline_state.alpha_blend_op = MTLBlendOperationAdd; + } + + /* Apply State. */ + pipeline_state.blending_enabled = (value != GPU_BLEND_NONE); + pipeline_state.src_rgb_blend_factor = src_rgb; + pipeline_state.dest_rgb_blend_factor = dst_rgb; + pipeline_state.src_alpha_blend_factor = src_alpha; + pipeline_state.dest_alpha_blend_factor = dst_alpha; + + /* Mark Dirty - Ensure context updates PSOs between draws. */ + pipeline_state.dirty_flags |= MTL_PIPELINE_STATE_PSO_FLAG; + pipeline_state.dirty = true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Memory barrier + * \{ */ + +/* NOTE(Metal): Granular option for specifying before/after stages for a barrier + * Would be a useful feature. */ +/*void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits, + eGPUStageBarrierBits before_stages, + eGPUStageBarrierBits after_stages) */ +void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits) +{ + /* NOTE(Metal): The Metal API implicitly tracks dependencies between resources. + * Memory barriers and execution barriers (Fences/Events) can be used to coordinate + * this explicitly, however, in most cases, the driver will be able to + * resolve these dependencies automatically. + * For untracked resources, such as MTLHeap's, explicit barriers are necessary. */ + eGPUStageBarrierBits before_stages = GPU_BARRIER_STAGE_ANY; + eGPUStageBarrierBits after_stages = GPU_BARRIER_STAGE_ANY; + + MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get()); + BLI_assert(ctx); + if (ctx->is_render_pass_active()) { + + /* Apple Silicon does not support memory barriers. + * We do not currently need these due to implicit API guarantees. + * NOTE(Metal): MTLFence/MTLEvent may be required to synchronize work if + * untracked resources are ever used. */ + if ([ctx->device hasUnifiedMemory]) { + return; + } + + /* Issue barrier. */ + /* TODO(Metal): To be completed pending implementation of RenderCommandEncoder management. */ + id<MTLRenderCommandEncoder> rec = nil; // ctx->get_active_render_command_encoder(); + BLI_assert(rec); + + /* Only supporting Metal on 10.15 onward anyway - Check required for warnings. */ + if (@available(macOS 10.14, *)) { + MTLBarrierScope scope = 0; + if (barrier_bits & GPU_BARRIER_SHADER_IMAGE_ACCESS || + barrier_bits & GPU_BARRIER_TEXTURE_FETCH) { + scope = scope | MTLBarrierScopeTextures | MTLBarrierScopeRenderTargets; + } + if (barrier_bits & GPU_BARRIER_SHADER_STORAGE || + barrier_bits & GPU_BARRIER_VERTEX_ATTRIB_ARRAY || + barrier_bits & GPU_BARRIER_ELEMENT_ARRAY) { + scope = scope | MTLBarrierScopeBuffers; + } + + MTLRenderStages before_stage_flags = 0; + MTLRenderStages after_stage_flags = 0; + if (before_stages & GPU_BARRIER_STAGE_VERTEX && + !(before_stages & GPU_BARRIER_STAGE_FRAGMENT)) { + before_stage_flags = before_stage_flags | MTLRenderStageVertex; + } + if (before_stages & GPU_BARRIER_STAGE_FRAGMENT) { + before_stage_flags = before_stage_flags | MTLRenderStageFragment; + } + if (after_stages & GPU_BARRIER_STAGE_VERTEX) { + after_stage_flags = after_stage_flags | MTLRenderStageVertex; + } + if (after_stages & GPU_BARRIER_STAGE_FRAGMENT) { + after_stage_flags = MTLRenderStageFragment; + } + + if (scope != 0) { + [rec memoryBarrierWithScope:scope + afterStages:after_stage_flags + beforeStages:before_stage_flags]; + } + } + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture State Management + * \{ */ + +void MTLStateManager::texture_unpack_row_length_set(uint len) +{ + /* Set source image row data stride when uploading image data to the GPU. */ + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + ctx->pipeline_state.unpack_row_length = len; +} + +void MTLStateManager::texture_bind(Texture *tex_, eGPUSamplerState sampler_type, int unit) +{ + BLI_assert(tex_); + gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_); + BLI_assert(mtl_tex); + + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + if (unit >= 0) { + ctx->texture_bind(mtl_tex, unit); + + /* Fetching textures default sampler configuration and applying + * eGPUSampler State on top. This path exists to support + * Any of the sampler state which is associated with the + * texture itself such as min/max mip levels. */ + MTLSamplerState sampler = mtl_tex->get_sampler_state(); + sampler.state = sampler_type; + + ctx->sampler_bind(sampler, unit); + } +} + +void MTLStateManager::texture_unbind(Texture *tex_) +{ + BLI_assert(tex_); + gpu::MTLTexture *mtl_tex = static_cast<gpu::MTLTexture *>(tex_); + BLI_assert(mtl_tex); + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + ctx->texture_unbind(mtl_tex); +} + +void MTLStateManager::texture_unbind_all(void) +{ + MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get())); + BLI_assert(ctx); + ctx->texture_unbind_all(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Binding (from image load store) + * \{ */ + +void MTLStateManager::image_bind(Texture *tex_, int unit) +{ + this->texture_bind(tex_, GPU_SAMPLER_DEFAULT, unit); +} + +void MTLStateManager::image_unbind(Texture *tex_) +{ + this->texture_unbind(tex_); +} + +void MTLStateManager::image_unbind_all(void) +{ + this->texture_unbind_all(); +} + +/** \} */ + +} // blender::gpu diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh index 47f03a5777b..b820256ec36 100644 --- a/source/blender/gpu/metal/mtl_texture.hh +++ b/source/blender/gpu/metal/mtl_texture.hh @@ -47,16 +47,13 @@ struct TextureUpdateRoutineSpecialisation { (component_count_input == other.component_count_input) && (component_count_output == other.component_count_output)); } -}; -template<> struct blender::DefaultHash<TextureUpdateRoutineSpecialisation> { - inline uint64_t operator()(const TextureUpdateRoutineSpecialisation &key) const + inline uint64_t hash() const { - - DefaultHash<std::string> string_hasher; + blender::DefaultHash<std::string> string_hasher; return (uint64_t)string_hasher( - key.input_data_type + key.output_data_type + - std::to_string((key.component_count_input << 8) + key.component_count_output)); + this->input_data_type + this->output_data_type + + std::to_string((this->component_count_input << 8) + this->component_count_output)); } }; @@ -78,12 +75,10 @@ struct DepthTextureUpdateRoutineSpecialisation { { return ((data_mode == other.data_mode)); } -}; -template<> struct blender::DefaultHash<DepthTextureUpdateRoutineSpecialisation> { - inline uint64_t operator()(const DepthTextureUpdateRoutineSpecialisation &key) const + inline uint64_t hash() const { - return (uint64_t)(key.data_mode); + return (uint64_t)(this->data_mode); } }; @@ -109,17 +104,14 @@ struct TextureReadRoutineSpecialisation { (component_count_output == other.component_count_output) && (depth_format_mode == other.depth_format_mode)); } -}; -template<> struct blender::DefaultHash<TextureReadRoutineSpecialisation> { - inline uint64_t operator()(const TextureReadRoutineSpecialisation &key) const + inline uint64_t hash() const { - - DefaultHash<std::string> string_hasher; - return (uint64_t)string_hasher(key.input_data_type + key.output_data_type + - std::to_string((key.component_count_input << 8) + - key.component_count_output + - (key.depth_format_mode << 28))); + blender::DefaultHash<std::string> string_hasher; + return (uint64_t)string_hasher(this->input_data_type + this->output_data_type + + std::to_string((this->component_count_input << 8) + + this->component_count_output + + (this->depth_format_mode << 28))); } }; @@ -158,21 +150,6 @@ typedef struct MTLSamplerState { const MTLSamplerState DEFAULT_SAMPLER_STATE = {GPU_SAMPLER_DEFAULT /*, 0, 9999*/}; -} // namespace blender::gpu - -template<> struct blender::DefaultHash<blender::gpu::MTLSamplerState> { - inline uint64_t operator()(const blender::gpu::MTLSamplerState &key) const - { - const DefaultHash<unsigned int> uint_hasher; - uint64_t main_hash = (uint64_t)uint_hasher((unsigned int)(key.state)); - - /* Hash other parameters as needed. */ - return main_hash; - } -}; - -namespace blender::gpu { - class MTLTexture : public Texture { friend class MTLContext; friend class MTLStateManager; diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm index 7ec3d390416..ca19d1f9e4b 100644 --- a/source/blender/gpu/metal/mtl_texture.mm +++ b/source/blender/gpu/metal/mtl_texture.mm @@ -1163,7 +1163,7 @@ void gpu::MTLTexture::mip_range_set(int min, int max) { BLI_assert(min <= max && min >= 0 && max <= mipmaps_); - /* Note: + /* NOTE: * - mip_min_ and mip_max_ are used to Clamp LODs during sampling. * - Given functions like Framebuffer::recursive_downsample modifies the mip range * between each layer, we do not want to be re-baking the texture. diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 17d9b392c69..1cd2301aa4e 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -419,10 +419,10 @@ static void detect_workarounds() GCaps.shader_storage_buffer_objects_support = false; } - /* Certain Intel based platforms don't clear the viewport textures. Always clearing leads to - * noticeable performance regressions. */ - if (GPU_type_matches( - GPU_DEVICE_INTEL, static_cast<eGPUOSType>(GPU_OS_MAC | GPU_OS_UNIX), GPU_DRIVER_ANY)) { + /* Certain Intel/AMD based platforms don't clear the viewport textures. Always clearing leads to + * noticeable performance regressions on other platforms as well. */ + if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY) || + GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_UNIX, GPU_DRIVER_ANY)) { GCaps.clear_viewport_workaround = true; } diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc index a3288ff4cff..f82138e0d65 100644 --- a/source/blender/gpu/opengl/gl_debug.cc +++ b/source/blender/gpu/opengl/gl_debug.cc @@ -331,11 +331,21 @@ void object_label(GLenum type, GLuint object, const char *name) char label[64]; SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type)); /* Small convenience for caller. */ - if (ELEM(type, GL_FRAGMENT_SHADER, GL_GEOMETRY_SHADER, GL_VERTEX_SHADER, GL_COMPUTE_SHADER)) { - type = GL_SHADER; - } - if (ELEM(type, GL_UNIFORM_BUFFER)) { - type = GL_BUFFER; + 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); } diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 957bc8b24be..5a28b8b7318 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -876,7 +876,7 @@ static char *glsl_patch_default_get() static char *glsl_patch_compute_get() { /** Used for shader patching. Init once. */ - static char patch[512] = "\0"; + static char patch[2048] = "\0"; if (patch[0] != '\0') { return patch; } @@ -889,6 +889,8 @@ static char *glsl_patch_compute_get() /* Array compat. */ STR_CONCAT(patch, slen, "#define gpu_Array(_type) _type[]\n"); + STR_CONCAT(patch, slen, datatoc_glsl_shader_defines_glsl); + BLI_assert(slen < sizeof(patch)); return patch; } diff --git a/source/blender/gpu/opengl/gl_storage_buffer.cc b/source/blender/gpu/opengl/gl_storage_buffer.cc index 109bb65fcb7..b30674fe5fa 100644 --- a/source/blender/gpu/opengl/gl_storage_buffer.cc +++ b/source/blender/gpu/opengl/gl_storage_buffer.cc @@ -144,6 +144,30 @@ void GLStorageBuf::clear(eGPUTextureFormat internal_format, eGPUDataFormat data_ } } +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); + } +} + /** \} */ } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_storage_buffer.hh b/source/blender/gpu/opengl/gl_storage_buffer.hh index c808a0bdda1..96052fe0065 100644 --- a/source/blender/gpu/opengl/gl_storage_buffer.hh +++ b/source/blender/gpu/opengl/gl_storage_buffer.hh @@ -36,6 +36,7 @@ class GLStorageBuf : public StorageBuf { void bind(int slot) override; void unbind() override; void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) override; + void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override; /* Special internal function to bind SSBOs to indirect argument targets. */ void bind_as(GLenum target); diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc index a7a0c92431f..6942a220892 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.cc +++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc @@ -5,6 +5,8 @@ * \ingroup gpu */ +#include "GPU_texture.h" + #include "gl_context.hh" #include "gl_vertex_buffer.hh" @@ -38,6 +40,7 @@ void GLVertBuf::release_data() } if (vbo_id_ != 0) { + GPU_TEXTURE_FREE_SAFE(buffer_texture_); GLContext::buf_free(vbo_id_); vbo_id_ = 0; memory_usage -= vbo_size_; @@ -51,6 +54,7 @@ void GLVertBuf::duplicate_data(VertBuf *dst_) BLI_assert(GLContext::get() != nullptr); GLVertBuf *src = this; GLVertBuf *dst = static_cast<GLVertBuf *>(dst_); + dst->buffer_texture_ = nullptr; if (src->vbo_id_ != 0) { dst->vbo_size_ = src->size_used_get(); @@ -111,6 +115,16 @@ void GLVertBuf::bind_as_ssbo(uint binding) glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_); } +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", wrap(this)); + } + GPU_texture_bind(buffer_texture_, binding); +} + const void *GLVertBuf::read() const { BLI_assert(is_active()); diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh index 4c29c17dcf7..e0a21587b60 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.hh +++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh @@ -11,18 +11,23 @@ #include "glew-mx.h" +#include "GPU_texture.h" + #include "gpu_vertex_buffer_private.hh" namespace blender { namespace gpu { class GLVertBuf : public VertBuf { - friend class GLTexture; /* For buffer texture. */ - friend class GLShader; /* For transform feedback. */ + friend class GLTexture; /* For buffer texture. */ + friend class GLShader; /* For transform feedback. */ + 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. */ + struct ::GPUTexture *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; @@ -46,6 +51,7 @@ class GLVertBuf : public VertBuf { void upload_data() override; void duplicate_data(VertBuf *dst) override; void bind_as_ssbo(uint binding) override; + void bind_as_texture(uint binding) override; private: bool is_active() const; diff --git a/source/blender/gpu/shaders/gpu_shader_geometry.glsl b/source/blender/gpu/shaders/gpu_shader_geometry.glsl deleted file mode 100644 index e0e899cfb35..00000000000 --- a/source/blender/gpu/shaders/gpu_shader_geometry.glsl +++ /dev/null @@ -1,93 +0,0 @@ - -#define INTERP_FACE_VARYING_2(result, fvarOffset, tessCoord) \ - { \ - vec2 v[4]; \ - int primOffset = (gl_PrimitiveID + PrimitiveIdBase) * 4; \ - for (int i = 0; i < 4; i++) { \ - int index = (primOffset + i) * osd_fvar_count + fvarOffset; \ - v[i] = vec2(texelFetch(FVarDataBuffer, index).s, texelFetch(FVarDataBuffer, index + 1).s); \ - } \ - result = mix(mix(v[0], v[1], tessCoord.s), mix(v[3], v[2], tessCoord.s), tessCoord.t); \ - } - -#define INTERP_FACE_VARYING_ATT_2(result, fvarOffset, tessCoord) \ - { \ - vec2 tmp; \ - INTERP_FACE_VARYING_2(tmp, fvarOffset, tessCoord); \ - result = vec3(tmp, 0); \ - } - -out block -{ - VertexData v; -} -outpt; - -void set_mtface_vertex_attrs(vec2 st); - -void emit_flat(int index, vec3 normal) -{ - outpt.v.position = inpt[index].v.position; - outpt.v.normal = normal; - - /* Compatibility */ - varnormal = outpt.v.normal; - varposition = outpt.v.position.xyz; - - /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); - vec2 st = quadst[index]; - - INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); - - set_mtface_vertex_attrs(st); - - gl_Position = ProjectionMatrix * inpt[index].v.position; - EmitVertex(); -} - -void emit_smooth(int index) -{ - outpt.v.position = inpt[index].v.position; - outpt.v.normal = inpt[index].v.normal; - - /* Compatibility */ - varnormal = outpt.v.normal; - varposition = outpt.v.position.xyz; - - /* TODO(sergey): Only uniform subdivisions atm. */ - vec2 quadst[4] = vec2[](vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1)); - vec2 st = quadst[index]; - - INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st); - - set_mtface_vertex_attrs(st); - - gl_Position = ProjectionMatrix * inpt[index].v.position; - EmitVertex(); -} - -void main() -{ - gl_PrimitiveID = gl_PrimitiveIDIn; - - if (osd_flat_shading) { - vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz; - vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz; - vec3 flat_normal = normalize(cross(B, A)); - emit_flat(0, flat_normal); - emit_flat(1, flat_normal); - emit_flat(3, flat_normal); - emit_flat(2, flat_normal); - } - else { - emit_smooth(0); - emit_smooth(1); - emit_smooth(3); - emit_smooth(2); - } - EndPrimitive(); -} - -void set_mtface_vertex_attrs(vec2 st) -{ |