| /* Copyright (c) 2017 Hans-Kristian Arntzen |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| // Based on the following files from the Granite rendering engine: |
| // - vulkan/command_buffer.hpp |
| |
| #ifndef LIB_ESCHER_THIRD_PARTY_GRANITE_VK_COMMAND_BUFFER_H_ |
| #define LIB_ESCHER_THIRD_PARTY_GRANITE_VK_COMMAND_BUFFER_H_ |
| |
| #include <vulkan/vulkan.hpp> |
| |
| #include "lib/escher/base/reffable.h" |
| #include "lib/escher/third_party/granite/vk/command_buffer_pipeline_state.h" |
| #include "lib/escher/third_party/granite/vk/pipeline_layout.h" |
| #include "lib/escher/util/enum_cast.h" |
| #include "lib/escher/vk/render_pass_info.h" |
| |
| // TODO(ES-83): CommandBuffer currently wraps an old-style impl::CommandBuffer. |
| #include "lib/escher/impl/command_buffer.h" |
| |
| namespace escher { |
| |
| class Escher; |
| |
| class CommandBuffer; |
| using CommandBufferPtr = fxl::RefPtr<CommandBuffer>; |
| |
| // CommandBuffer is a wrapper around VkCommandBuffer, which significantly |
| // improves upon the usability of the raw Vulkan object in a number of ways. |
| // |
| // Most notably, CommandBuffer provides an "OpenGL-like" approach to resource |
| // binding and pipeline generation. Users of CommandBuffer never directly deal |
| // with VkPipelines, VkRenderPasses, VkFramebuffers, and others; these are |
| // created behind the scenes, and cached for efficiency. For example, the exact |
| // same shader code might require muliple VkPipeline variants to be generated, |
| // for example if different depth-comparison ops are to be used. CommandBuffer |
| // frees clients of the burden of manually generating and managing these |
| // VkPipeline variants. Instead, clients simply call SetShaderProgram(), and |
| // the appropriate variants are lazily generated when necessary, based on the |
| // ShaderProgram and other CommandBuffer state (e.g. depth/stencil state, the |
| // strides/formats/offsets of currently-bound vertex buffers, etc.). |
| // |
| // Another major convenience provided by CommandBuffer is life-cycle management |
| // of resources that are no longer needed. Vulkan forbids client applications |
| // from destroying any resource that is referenced by a "pending command buffer" |
| // (i.e. one whose commands have not finished executing on the GPU). Instead of |
| // being destroyed immediately, resources whose ref-counts reach zero are kept |
| // alive until all command buffers that reference them have finished executing. |
| // |
| // Not thread safe. |
| class CommandBuffer : public Reffable { |
| public: |
| enum class Type { kGraphics = 0, kCompute, kTransfer, kEnumCount }; |
| |
| // Constructors. |
| static CommandBufferPtr NewForType(Escher* escher, Type type); |
| static CommandBufferPtr NewForGraphics(Escher* escher); |
| static CommandBufferPtr NewForCompute(Escher* escher); |
| static CommandBufferPtr NewForTransfer(Escher* escher); |
| |
| Type type() const { return type_; } |
| |
| vk::CommandBuffer vk() const { return vk_; } |
| vk::Device vk_device() const { return vk_device_; } |
| // TODO(ES-83): deprecated from the get-go. |
| impl::CommandBuffer* impl() const { return impl_; } |
| |
| // Submits the command buffer on the appropriate queue: the main queue for |
| // graphics and compute tasks, and the transfer queue for dedicated transfer |
| // operations. |
| // |
| // TODO(ES-83): this is a placeholder; the submission API will be refined. |
| bool Submit(CommandBufferFinishedCallback callback); |
| |
| // Wraps vkCmdBeginRenderPass(). Uses |info| to obtain a cached VkRenderPass |
| // and VkFramebuffer. |
| void BeginRenderPass(const RenderPassInfo& info); |
| |
| // Wraps vkCmdEndRenderPass(). |
| void EndRenderPass(); |
| |
| // Wraps vkCmdPipelineBarrier(), using a barrier consisting of a single |
| // VkImageMemoryBarrier. Keeps |image| alive while command buffer is pending. |
| void ImageBarrier(const ImagePtr& image, vk::ImageLayout old_layout, |
| vk::ImageLayout new_layout, |
| vk::PipelineStageFlags src_stages, |
| vk::AccessFlags src_access, |
| vk::PipelineStageFlags dst_stages, |
| vk::AccessFlags dst_access); |
| |
| // Defers call to vkCmdPushConstants() via kDirtyPushConstantsBit. |
| void PushConstants(const void* data, vk::DeviceSize offset, |
| vk::DeviceSize range); |
| template <typename StructT> |
| void PushConstants(const StructT* data, vk::DeviceSize offset = 0U) { |
| PushConstants(data, offset, sizeof(StructT)); |
| } |
| template <typename StructT> |
| void PushConstants(const StructT& data, vk::DeviceSize offset = 0U) { |
| PushConstants(&data, offset, sizeof(StructT)); |
| } |
| |
| // Set/dirty a uniform buffer binding that will later be flushed, causing |
| // descriptor sets to be written/bound as necessary. Keeps |buffer| alive |
| // while command buffer is pending. |
| void BindUniformBuffer(uint32_t set, uint32_t binding, |
| const BufferPtr& buffer); |
| void BindUniformBuffer(uint32_t set, uint32_t binding, |
| const BufferPtr& buffer, vk::DeviceSize offset, |
| vk::DeviceSize range); |
| void BindUniformBuffer(uint32_t set, uint32_t binding, Buffer* buffer, |
| vk::DeviceSize offset, vk::DeviceSize range); |
| |
| // Set/dirty a texture binding that will later be flushed, causing descriptor |
| // sets to be written/bound as necessary. Keeps |texture| alive while command |
| // buffer is pending. |
| void BindTexture(unsigned set, unsigned binding, Texture* texture); |
| void BindTexture(unsigned set, unsigned binding, const TexturePtr& texture) { |
| BindTexture(set, binding, texture.get()); |
| } |
| |
| // Set/dirty a vertex buffer binding that will later be flushed, causing |
| // descriptor sets to be written/bound as necessary. |
| void BindVertices( |
| uint32_t binding, vk::Buffer buffer, vk::DeviceSize offset, |
| vk::DeviceSize stride, |
| vk::VertexInputRate step_rate = vk::VertexInputRate::eVertex); |
| // These two variants keep |buffer| alive while the command buffer is pending; |
| // the one above makes this the responsibility of the caller. |
| void BindVertices( |
| uint32_t binding, Buffer* buffer, vk::DeviceSize offset, |
| vk::DeviceSize stride, |
| vk::VertexInputRate step_rate = vk::VertexInputRate::eVertex); |
| void BindVertices( |
| uint32_t binding, const BufferPtr& buffer, vk::DeviceSize offset, |
| vk::DeviceSize stride, |
| vk::VertexInputRate step_rate = vk::VertexInputRate::eVertex) { |
| BindVertices(binding, buffer.get(), offset, stride, step_rate); |
| } |
| |
| // Sets the current index buffer binding; this happens immediately because |
| // index buffer changes never require descriptor sets to be written or new |
| // pipelines to be generated. |
| void BindIndices(vk::Buffer buffer, vk::DeviceSize offset, |
| vk::IndexType index_type); |
| // This variant keeps |buffer| alive while command buffer is pending. |
| void BindIndices(const BufferPtr& buffer, vk::DeviceSize offset, |
| vk::IndexType index_type); |
| |
| // Set/dirty the attributes that will be used to interpret the vertex buffer |
| // at |binding| (see BindVertices() above) when the next draw call is made. |
| void SetVertexAttributes(uint32_t binding, uint32_t attrib, vk::Format format, |
| vk::DeviceSize offset) { |
| FXL_DCHECK(IsInRenderPass()); |
| pipeline_state_.SetVertexAttributes(binding, attrib, format, offset); |
| SetDirty(kDirtyStaticVertexBit); |
| } |
| |
| // Wraps vkCmdDrawIndexed(), first flushing any dirty render state; this may |
| // cause descriptor sets to be written/bound, or a new pipeline to be created. |
| void DrawIndexed(uint32_t index_count, uint32_t instance_count = 1, |
| uint32_t first_index = 0, int32_t vertex_offset = 0, |
| uint32_t first_instance = 0); |
| |
| // Wraps vkCmdClearAttachments(). Clears the specified rectangle of the |
| // specified attachment (see RenderPassInfo), filling it with the specified |
| // values. |
| void ClearAttachmentRect(uint32_t subpass_color_attachment_index, |
| const vk::ClearRect& rect, |
| const vk::ClearValue& value, |
| vk::ImageAspectFlags aspect); |
| // Convenient version of ClearAttachmentRect() for color attachments. |
| // NOTE: uses baseArrayLayer == 0 and layerCount == 1. |
| void ClearColorAttachmentRect(uint32_t subpass_color_attachment_index, |
| vk::Offset2D offset, vk::Extent2D extent, |
| const vk::ClearColorValue& value); |
| // Convenient version of ClearAttachmentRect() for depth/stencil attachments. |
| // NOTE: uses baseArrayLayer == 0 and layerCount == 1. |
| void ClearDepthStencilAttachmentRect(vk::Offset2D offset, vk::Extent2D extent, |
| const vk::ClearDepthStencilValue& value, |
| vk::ImageAspectFlags aspect); |
| |
| // Convenient way to bring CommandBuffer to a known default state. See the |
| // implementation of SetToDefaultState() for more details; it's basically a |
| // big switch statement. |
| enum class DefaultState { kOpaque, kTranslucent }; |
| void SetToDefaultState(DefaultState state); |
| |
| // Set the ShaderProgram that will be used to obtain the VkPipeline to be used |
| // by the next draw-call or compute dispatch. |
| void SetShaderProgram(ShaderProgram* program); |
| void SetShaderProgram(const ShaderProgramPtr& program) { |
| SetShaderProgram(program.get()); |
| } |
| |
| // Set the viewport. Must be called within a render pass. |
| void SetViewport(const vk::Viewport& viewport); |
| |
| // Set the scissor rect. Must be called within a render pass. |
| void SetScissor(const vk::Rect2D& rect); |
| |
| // The following functions set static state that might result in generation of |
| // a new pipeline variant. |
| |
| void SetBlendConstants(const float blend_constants[4]); |
| void SetBlendEnable(bool blend_enable); |
| void SetBlendFactors(vk::BlendFactor src_color_blend, |
| vk::BlendFactor src_alpha_blend, |
| vk::BlendFactor dst_color_blend, |
| vk::BlendFactor dst_alpha_blend); |
| void SetBlendFactors(vk::BlendFactor src_blend, vk::BlendFactor dst_blend); |
| void SetBlendOp(vk::BlendOp color_blend_op, vk::BlendOp alpha_blend_op); |
| void SetBlendOp(vk::BlendOp blend_op); |
| |
| // Packs vk::ColorComponentFlags for many color attachments into a 32-bit int. |
| // Each attachment uses 4 bits, one for each of the RGBA components, for a |
| // maximum of 8 attachments. Not coincidentally, this is the value of |
| // VulkanLimits::kNumColorAttachments. Color attachment #0 is stored in the |
| // least-significant 4 bits. |
| void SetColorWriteMask(uint32_t color_write_mask); |
| |
| void SetCullMode(vk::CullModeFlags cull_mode); |
| |
| void SetDepthBias(bool depth_bias_enable); |
| void SetDepthBias(float depth_bias_constant, float depth_bias_slope); |
| void SetDepthCompareOp(vk::CompareOp depth_compare); |
| void SetDepthTestAndWrite(bool depth_test, bool depth_write); |
| |
| void SetFrontFace(vk::FrontFace front_face); |
| |
| void SetMultisampleState(bool alpha_to_coverage, bool alpha_to_one = false, |
| bool sample_shading = false); |
| |
| void SetStencilBackOps(vk::CompareOp stencil_back_compare_op, |
| vk::StencilOp stencil_back_pass, |
| vk::StencilOp stencil_back_fail, |
| vk::StencilOp stencil_back_depth_fail); |
| void SetStencilBackReference(uint8_t back_compare_mask, |
| uint8_t back_write_mask, uint8_t back_reference); |
| void SetStencilFrontOps(vk::CompareOp stencil_front_compare_op, |
| vk::StencilOp stencil_front_pass, |
| vk::StencilOp stencil_front_fail, |
| vk::StencilOp stencil_front_depth_fail); |
| void SetStencilFrontReference(uint8_t front_compare_mask, |
| uint8_t front_write_mask, |
| uint8_t front_reference); |
| void SetStencilOps(vk::CompareOp stencil_compare_op, |
| vk::StencilOp stencil_pass, vk::StencilOp stencil_fail, |
| vk::StencilOp stencil_depth_fail); |
| void SetStencilTest(bool stencil_test); |
| |
| void SetPrimitiveRestart(bool primitive_restart); |
| void SetPrimitiveTopology(vk::PrimitiveTopology primitive_topology); |
| void SetWireframe(bool wireframe); |
| |
| // State. Clients don't need to worry about this; these are only used |
| // internally. The only reason that they're not private is that they are |
| // aggregated in SavedState. |
| // |
| // TODO(ES-83): SavedState is not yet used. |
| // TODO(ES-83): Experiment with making them private except for SavedState, |
| // which means that SavedState would be a fully-opaque representation. |
| |
| // TODO(ES-83): Not saved in SavedState. Should it be? Otherwise, make |
| // private? |
| struct IndexBindingState { |
| vk::Buffer buffer; |
| vk::DeviceSize offset; |
| vk::IndexType index_type; |
| }; |
| |
| // Resource binding info for a single Vulkan descriptor. When flushed by |
| // FlushRenderState(), the type of the union value is resolved by using the |
| // masks in the current impl::DescriptorSetLayout. |
| struct DescriptorBindingInfo { |
| union { |
| vk::DescriptorBufferInfo buffer; |
| struct { |
| vk::DescriptorImageInfo fp; |
| vk::DescriptorImageInfo integer; |
| } image; |
| vk::BufferView buffer_view; |
| }; |
| }; |
| |
| // Aggregates bindings for all descriptors in a single descriptor set. This |
| // includes: |
| // - the specific Vulkan resource(s) to be bound (samplers, buffers, images) |
| // - the IDs of the corresponding Escher resources from which the Vulkan |
| // resources are obtained. |
| struct DescriptorSetBindings { |
| DescriptorBindingInfo infos[VulkanLimits::kNumBindings]; |
| uint64_t uids[VulkanLimits::kNumBindings]; |
| uint64_t secondary_uids[VulkanLimits::kNumBindings]; |
| }; |
| |
| // Aggregates bindings for all descriptor sets, as well as push constant data. |
| struct ResourceBindings { |
| DescriptorSetBindings descriptor_sets[VulkanLimits::kNumDescriptorSets]; |
| uint8_t push_constant_data[VulkanLimits::kPushConstantSize]; |
| }; |
| |
| using PipelineStaticState = CommandBufferPipelineState::StaticState; |
| using PipelinePotentialStaticState = |
| CommandBufferPipelineState::PotentialStaticState; |
| |
| // State that can be changed dynamically without requiring pipeline changes. |
| struct DynamicState { |
| float depth_bias_constant = 0.0f; |
| float depth_bias_slope = 0.0f; |
| uint8_t front_compare_mask = 0; |
| uint8_t front_write_mask = 0; |
| uint8_t front_reference = 0; |
| uint8_t back_compare_mask = 0; |
| uint8_t back_write_mask = 0; |
| uint8_t back_reference = 0; |
| }; |
| |
| // Flags that identify the specific state that is saved in a SavedState (any |
| // other state is undefined and should not be used). |
| enum SavedStateBits { |
| kSavedBindingsBit0 = 1u << 0, |
| kSavedBindingsBit1 = 1u << 1, |
| kSavedBindingsBit2 = 1u << 2, |
| kSavedBindingsBit3 = 1u << 3, |
| kSavedViewportBit = 1u << 4, |
| kSavedScissorBit = 1u << 5, |
| kSavedRenderStateBit = 1u << 6, |
| kSavedPushConstantBit = 1u << 7, |
| }; |
| using SavedStateFlags = uint32_t; |
| |
| // SavedStateFlags sets aside only 4 bits to indicate which descriptor set |
| // bindings are to be saved. Should we desire a larger number of descriptor |
| // sets in the future, more bits must be allocated for this purpose. |
| static_assert( |
| VulkanLimits::kNumDescriptorSets == 4, |
| "Not enough bits to indicate which descriptor set bindings to save."); |
| |
| // Saves state so that it can be restored later. |
| struct SavedState { |
| SavedStateFlags flags = 0; |
| ResourceBindings bindings; |
| vk::Viewport viewport; |
| vk::Rect2D scissor; |
| |
| PipelineStaticState static_state; |
| PipelinePotentialStaticState potential_static_state; |
| DynamicState dynamic_state; |
| }; |
| |
| void SaveState(SavedStateFlags flags, SavedState* state) const; |
| void RestoreState(const CommandBuffer::SavedState& state); |
| |
| private: |
| friend class VulkanTester; |
| |
| // Used to track which state must be flushed by FlushGraphicsState(). |
| enum DirtyBits { |
| kDirtyStaticStateBit = 1 << 0, |
| kDirtyPipelineBit = 1 << 1, |
| kDirtyViewportBit = 1 << 2, |
| kDirtyScissorBit = 1 << 3, |
| kDirtyDepthBiasBit = 1 << 4, |
| // Indicates that the stencil reference value and write/compare masks are |
| // dirty, for both front- and back-facing stencil tests. |
| kDirtyStencilMasksAndReferenceBit = 1 << 5, |
| kDirtyStaticVertexBit = 1 << 6, |
| kDirtyPushConstantsBit = 1 << 7, |
| // The pipelines that CommandBufferPipelineState::BuildGraphicsPipeline() |
| // produces always treats viewport, scissor, stencil, and depth-bias as |
| // dynamic state. |
| kDirtyDynamicBits = kDirtyViewportBit | kDirtyScissorBit | |
| kDirtyDepthBiasBit | kDirtyStencilMasksAndReferenceBit, |
| }; |
| using DirtyFlags = uint32_t; |
| |
| // TODO(ES-83): impl::CommandBuffer is deprecated from the get-go. |
| CommandBuffer(EscherWeakPtr escher, Type type, |
| impl::CommandBuffer* command_buffer); |
| |
| // Sets all flags to dirty, and zeros out DescriptorSetBindings uids. |
| void BeginGraphicsOrComputeContext(); |
| |
| // Called by BeginRenderPass(), calls BeginGraphicsOrComputeContext(). |
| void BeginGraphics(); |
| |
| // Called by EndRenderPass(): any time we're not processing graphics commands, |
| // we are assumed to be processing compute tasks. Calls |
| // BeginGraphicsOrComputeContext(). |
| void BeginCompute(); |
| |
| // Return true if BeginRenderPass() has been called more recently than |
| // EndRenderPass(). |
| bool IsInRenderPass(); |
| |
| // Called immediately before draw calls are made, e.g. by DrawIndexed(). |
| // Depending on which dirty flags are set, may call FlushGraphicsPipeline() |
| // and FlushDescriptorSet(), as well as calling Vulkan setters for dynamic |
| // state such as viewport, scissor, depth-bias, etc. |
| void FlushRenderState(); |
| |
| // Called by FlushRenderState() and FlushComputeState(). Flushes all dirty |
| // descriptor sets that are required by the current PipelineLayout. |
| void FlushDescriptorSets(); |
| void FlushDescriptorSet(uint32_t set_index); |
| |
| // Called by FlushDescriptorSet() when one or more descriptors in the set must |
| // be updated. |
| void WriteDescriptors(uint32_t set_index, vk::DescriptorSet vk_set, |
| const impl::DescriptorSetLayout& set_layout); |
| |
| // Called when there is the possibility that a pipeline change may be |
| // required. A hash is generated from the currently-enabled vertex attributes |
| // (i.e. those that are used by the current pipeline layout), as well as the |
| // current subpass index, and other "static" state. This hash is used to |
| // look up a cached pipeline. If no pipeline is available, then a new one is |
| // built; see CommandBufferPipelineState()::BuildGraphicsPipeline(). |
| void FlushGraphicsPipeline(); |
| |
| // Set the specified dirty flag bits. |
| void SetDirty(DirtyFlags flags) { dirty_ |= flags; } |
| |
| // Return the subset of |flags| that is dirty, and clear only those flags so |
| // that they are no longer dirty. |
| DirtyFlags GetAndClearDirty(DirtyFlags flags) { |
| auto mask = dirty_ & flags; |
| dirty_ &= ~flags; |
| return mask; |
| } |
| |
| // Used internally by the various Bind*() methods. |
| CommandBuffer::DescriptorSetBindings* GetDescriptorSetBindings( |
| uint32_t set_index) { |
| FXL_DCHECK(set_index < VulkanLimits::kNumDescriptorSets); |
| return &(bindings_.descriptor_sets[set_index]); |
| } |
| |
| // Used internally by the various Bind*() methods. |
| CommandBuffer::DescriptorBindingInfo* GetDescriptorBindingInfo( |
| uint32_t set_index, uint32_t binding_index) { |
| FXL_DCHECK(binding_index < VulkanLimits::kNumBindings); |
| return &(GetDescriptorSetBindings(set_index)->infos[binding_index]); |
| } |
| |
| // Used internally by the various Bind*() methods. |
| CommandBuffer::DescriptorBindingInfo* GetDescriptorBindingInfo( |
| CommandBuffer::DescriptorSetBindings* set_bindings, |
| uint32_t binding_index) { |
| FXL_DCHECK(binding_index < VulkanLimits::kNumBindings); |
| return &(set_bindings->infos[binding_index]); |
| } |
| |
| EscherWeakPtr const escher_; |
| Type type_; |
| |
| // TODO(ES-83): deprecated from the get-go. |
| impl::CommandBuffer* const impl_; |
| vk::CommandBuffer vk_; |
| vk::Device vk_device_; |
| |
| DirtyFlags dirty_ = ~0u; |
| uint32_t dirty_descriptor_sets_ = 0; |
| |
| bool is_compute_ = false; |
| |
| CommandBufferPipelineState pipeline_state_; |
| DynamicState dynamic_state_ = {}; |
| IndexBindingState index_binding_ = {}; |
| ResourceBindings bindings_ = {}; |
| |
| impl::FramebufferPtr framebuffer_; |
| |
| vk::Pipeline current_vk_pipeline_; |
| vk::PipelineLayout current_vk_pipeline_layout_; |
| |
| ShaderProgram* current_program_ = nullptr; |
| PipelineLayout* current_pipeline_layout_ = nullptr; |
| |
| vk::Viewport viewport_ = {}; |
| vk::Rect2D scissor_ = {}; |
| |
| }; // namespace escher |
| |
| // Inline function definitions. |
| |
| #if defined(SET_STATIC_STATE) || defined(SET_STATIC_STATE_ENUM) || \ |
| defined(SET_POTENTIALLY_STATIC_STATE) || defined(SET_DYNAMIC_STATE) |
| #error CommandBuffer state macros already defined. |
| #endif |
| |
| #define SET_STATIC_STATE(value) \ |
| do { \ |
| if (pipeline_state_.static_state()->value != value) { \ |
| pipeline_state_.static_state()->value = value; \ |
| SetDirty(kDirtyStaticStateBit); \ |
| } \ |
| } while (0) |
| |
| #define SET_STATIC_STATE_ENUM(value) \ |
| do { \ |
| auto enum_value = EnumCast(value); \ |
| if (pipeline_state_.static_state()->value != enum_value) { \ |
| pipeline_state_.static_state()->value = enum_value; \ |
| SetDirty(kDirtyStaticStateBit); \ |
| } \ |
| } while (0) |
| |
| #define SET_POTENTIALLY_STATIC_STATE(value) \ |
| do { \ |
| if (pipeline_state_.potential_static_state()->value != value) { \ |
| pipeline_state_.potential_static_state()->value = value; \ |
| SetDirty(kDirtyStaticStateBit); \ |
| } \ |
| } while (0) |
| |
| #define SET_DYNAMIC_STATE(state, flags) \ |
| do { \ |
| if (dynamic_state_.state != state) { \ |
| dynamic_state_.state = state; \ |
| SetDirty(flags); \ |
| } \ |
| } while (0) |
| |
| inline void CommandBuffer::SetViewport(const vk::Viewport& viewport) { |
| // Must be called in render pass, because BeginRenderPass() sets the scissor |
| // region, and confusion might result if a client didn't realize this and |
| // tried to set it outside of a render pass. |
| FXL_DCHECK(IsInRenderPass()); |
| viewport_ = viewport; |
| SetDirty(kDirtyViewportBit); |
| } |
| |
| inline void CommandBuffer::SetScissor(const vk::Rect2D& rect) { |
| // Must be called in render pass, because BeginRenderPass() sets the viewport, |
| // and confusion might result if a client didn't realize this and tried to |
| // set it outside of a render pass. |
| FXL_DCHECK(IsInRenderPass()); |
| FXL_DCHECK(rect.offset.x >= 0); |
| FXL_DCHECK(rect.offset.y >= 0); |
| scissor_ = rect; |
| SetDirty(kDirtyScissorBit); |
| } |
| |
| inline void CommandBuffer::SetDepthTestAndWrite(bool depth_test, |
| bool depth_write) { |
| SET_STATIC_STATE(depth_test); |
| SET_STATIC_STATE(depth_write); |
| } |
| |
| inline void CommandBuffer::SetWireframe(bool wireframe) { |
| SET_STATIC_STATE(wireframe); |
| } |
| |
| inline void CommandBuffer::SetDepthCompareOp(vk::CompareOp depth_compare) { |
| SET_STATIC_STATE_ENUM(depth_compare); |
| } |
| |
| inline void CommandBuffer::SetBlendEnable(bool blend_enable) { |
| SET_STATIC_STATE(blend_enable); |
| } |
| |
| inline void CommandBuffer::SetBlendFactors(vk::BlendFactor src_color_blend, |
| vk::BlendFactor src_alpha_blend, |
| vk::BlendFactor dst_color_blend, |
| vk::BlendFactor dst_alpha_blend) { |
| SET_STATIC_STATE_ENUM(src_color_blend); |
| SET_STATIC_STATE_ENUM(dst_color_blend); |
| SET_STATIC_STATE_ENUM(src_alpha_blend); |
| SET_STATIC_STATE_ENUM(dst_alpha_blend); |
| } |
| |
| inline void CommandBuffer::SetBlendFactors(vk::BlendFactor src_blend, |
| vk::BlendFactor dst_blend) { |
| SetBlendFactors(src_blend, src_blend, dst_blend, dst_blend); |
| } |
| |
| inline void CommandBuffer::SetBlendOp(vk::BlendOp color_blend_op, |
| vk::BlendOp alpha_blend_op) { |
| SET_STATIC_STATE_ENUM(color_blend_op); |
| SET_STATIC_STATE_ENUM(alpha_blend_op); |
| } |
| |
| inline void CommandBuffer::SetBlendOp(vk::BlendOp blend_op) { |
| SetBlendOp(blend_op, blend_op); |
| } |
| |
| inline void CommandBuffer::SetColorWriteMask(uint32_t color_write_mask) { |
| SET_STATIC_STATE(color_write_mask); |
| } |
| |
| inline void CommandBuffer::SetDepthBias(bool depth_bias_enable) { |
| SET_STATIC_STATE(depth_bias_enable); |
| } |
| |
| inline void CommandBuffer::SetStencilTest(bool stencil_test) { |
| SET_STATIC_STATE(stencil_test); |
| } |
| |
| inline void CommandBuffer::SetStencilFrontOps( |
| vk::CompareOp stencil_front_compare_op, vk::StencilOp stencil_front_pass, |
| vk::StencilOp stencil_front_fail, vk::StencilOp stencil_front_depth_fail) { |
| SET_STATIC_STATE_ENUM(stencil_front_compare_op); |
| SET_STATIC_STATE_ENUM(stencil_front_pass); |
| SET_STATIC_STATE_ENUM(stencil_front_fail); |
| SET_STATIC_STATE_ENUM(stencil_front_depth_fail); |
| } |
| |
| inline void CommandBuffer::SetStencilBackOps( |
| vk::CompareOp stencil_back_compare_op, vk::StencilOp stencil_back_pass, |
| vk::StencilOp stencil_back_fail, vk::StencilOp stencil_back_depth_fail) { |
| SET_STATIC_STATE_ENUM(stencil_back_compare_op); |
| SET_STATIC_STATE_ENUM(stencil_back_pass); |
| SET_STATIC_STATE_ENUM(stencil_back_fail); |
| SET_STATIC_STATE_ENUM(stencil_back_depth_fail); |
| } |
| |
| inline void CommandBuffer::SetStencilOps(vk::CompareOp stencil_compare_op, |
| vk::StencilOp stencil_pass, |
| vk::StencilOp stencil_fail, |
| vk::StencilOp stencil_depth_fail) { |
| SetStencilFrontOps(stencil_compare_op, stencil_pass, stencil_fail, |
| stencil_depth_fail); |
| SetStencilBackOps(stencil_compare_op, stencil_pass, stencil_fail, |
| stencil_depth_fail); |
| } |
| |
| inline void CommandBuffer::SetPrimitiveTopology( |
| vk::PrimitiveTopology primitive_topology) { |
| SET_STATIC_STATE_ENUM(primitive_topology); |
| } |
| |
| inline void CommandBuffer::SetPrimitiveRestart(bool primitive_restart) { |
| SET_STATIC_STATE(primitive_restart); |
| } |
| |
| inline void CommandBuffer::SetMultisampleState(bool alpha_to_coverage, |
| bool alpha_to_one, |
| bool sample_shading) { |
| SET_STATIC_STATE(alpha_to_coverage); |
| SET_STATIC_STATE(alpha_to_one); |
| SET_STATIC_STATE(sample_shading); |
| } |
| |
| inline void CommandBuffer::SetFrontFace(vk::FrontFace front_face) { |
| SET_STATIC_STATE_ENUM(front_face); |
| } |
| |
| inline void CommandBuffer::SetCullMode(vk::CullModeFlags vk_cull_mode) { |
| auto cull_mode = static_cast<VkCullModeFlags>(vk_cull_mode); |
| SET_STATIC_STATE(cull_mode); |
| } |
| |
| inline void CommandBuffer::SetBlendConstants(const float blend_constants[4]) { |
| SET_POTENTIALLY_STATIC_STATE(blend_constants[0]); |
| SET_POTENTIALLY_STATIC_STATE(blend_constants[1]); |
| SET_POTENTIALLY_STATIC_STATE(blend_constants[2]); |
| SET_POTENTIALLY_STATIC_STATE(blend_constants[3]); |
| } |
| |
| inline void CommandBuffer::SetDepthBias(float depth_bias_constant, |
| float depth_bias_slope) { |
| SET_DYNAMIC_STATE(depth_bias_constant, kDirtyDepthBiasBit); |
| SET_DYNAMIC_STATE(depth_bias_slope, kDirtyDepthBiasBit); |
| } |
| |
| inline void CommandBuffer::SetStencilFrontReference(uint8_t front_compare_mask, |
| uint8_t front_write_mask, |
| uint8_t front_reference) { |
| SET_DYNAMIC_STATE(front_compare_mask, kDirtyStencilMasksAndReferenceBit); |
| SET_DYNAMIC_STATE(front_write_mask, kDirtyStencilMasksAndReferenceBit); |
| SET_DYNAMIC_STATE(front_reference, kDirtyStencilMasksAndReferenceBit); |
| } |
| |
| inline void CommandBuffer::SetStencilBackReference(uint8_t back_compare_mask, |
| uint8_t back_write_mask, |
| uint8_t back_reference) { |
| SET_DYNAMIC_STATE(back_compare_mask, kDirtyStencilMasksAndReferenceBit); |
| SET_DYNAMIC_STATE(back_write_mask, kDirtyStencilMasksAndReferenceBit); |
| SET_DYNAMIC_STATE(back_reference, kDirtyStencilMasksAndReferenceBit); |
| } |
| |
| #undef SET_STATIC_STATE |
| #undef SET_STATIC_STATE_ENUM |
| #undef SET_POTENTIALLY_STATIC_STATE |
| #undef SET_DYNAMIC_STATE |
| |
| } // namespace escher |
| |
| #endif // LIB_ESCHER_THIRD_PARTY_GRANITE_VK_COMMAND_BUFFER_H_ |