Compare commits
1 Commits
kpiascik/s
...
pf/add-dox
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edb77fe6a9 |
764
filament/backend/include/private/backend/DriverAPI.dox
Normal file
764
filament/backend/include/private/backend/DriverAPI.dox
Normal file
@@ -0,0 +1,764 @@
|
||||
/** @file DriverAPI.dox
|
||||
* @brief External documentation for the backend::Driver API.
|
||||
*
|
||||
* This file contains Doxygen documentation for the functions declared in DriverAPI.inc.
|
||||
* This is used to keep the documentation separate from the macro-heavy header file.
|
||||
*/
|
||||
|
||||
// General lifecycle and frame management
|
||||
|
||||
/** @fn Driver::tick()
|
||||
* @brief Called periodically by the system to perform maintenance tasks.
|
||||
*/
|
||||
|
||||
/** @fn Driver::beginFrame(int64_t monotonic_clock_ns, int64_t refreshIntervalNs, uint32_t frameId)
|
||||
* @brief Signals the beginning of a new frame to the driver.
|
||||
* @param monotonic_clock_ns The current monotonic clock time in nanoseconds.
|
||||
* @param refreshIntervalNs The display's refresh interval in nanoseconds.
|
||||
* @param frameId A unique identifier for the new frame.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setFrameScheduledCallback(backend::SwapChainHandle sch, backend::CallbackHandler* handler, backend::FrameScheduledCallback&& callback, uint64_t flags)
|
||||
* @brief Schedules a callback to be executed when the frame associated with the given swap chain has been scheduled for presentation.
|
||||
* @param sch The handle of the swap chain.
|
||||
* @param handler The callback handler that will execute the callback.
|
||||
* @param callback The function to be called.
|
||||
* @param flags Flags to control the callback behavior.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setFrameCompletedCallback(backend::SwapChainHandle sch, backend::CallbackHandler* handler, utils::Invocable<void(void)>&& callback)
|
||||
* @brief Sets a callback to be executed when rendering to the given swap chain is complete for the current frame.
|
||||
* @param sch The handle of the swap chain.
|
||||
* @param handler The callback handler that will execute the callback.
|
||||
* @param callback The function to be called upon frame completion.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setPresentationTime(int64_t monotonic_clock_ns)
|
||||
* @brief Informs the driver of the intended presentation time for the current frame.
|
||||
* @param monotonic_clock_ns The desired presentation time in nanoseconds, based on the monotonic clock.
|
||||
*/
|
||||
|
||||
/** @fn Driver::endFrame(uint32_t frameId)
|
||||
* @brief Signals the end of a frame to the driver.
|
||||
* @param frameId The unique identifier of the frame that is ending.
|
||||
*/
|
||||
|
||||
/** @fn Driver::flush()
|
||||
* @brief Submits all pending commands to the GPU for execution, without waiting for them to complete.
|
||||
*/
|
||||
|
||||
/** @fn Driver::finish()
|
||||
* @brief Submits all pending commands and waits for the GPU to finish executing them.
|
||||
*/
|
||||
|
||||
/** @fn Driver::resetState()
|
||||
* @brief Resets any cached or tracked driver state, forcing the driver to re-evaluate and set all states.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setDebugTag(backend::HandleBase::HandleId handleId, utils::CString tag)
|
||||
* @brief Associates a user-provided string tag with a driver handle for debugging purposes.
|
||||
* @param handleId The ID of the handle to tag.
|
||||
* @param tag The debug string to associate with the handle.
|
||||
*/
|
||||
|
||||
// Resource creation
|
||||
|
||||
/** @fn backend::VertexBufferInfoHandle Driver::createVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, backend::AttributeArray attributes)
|
||||
* @brief Creates a VertexBufferInfo object which describes the layout of a vertex buffer.
|
||||
* @param bufferCount The number of buffer objects in the vertex buffer.
|
||||
* @param attributeCount The number of attributes in the vertex buffer.
|
||||
* @param attributes An array describing each vertex attribute.
|
||||
* @return A handle to the new VertexBufferInfo object.
|
||||
*/
|
||||
|
||||
/** @fn backend::VertexBufferHandle Driver::createVertexBuffer(uint32_t vertexCount, backend::VertexBufferInfoHandle vbih)
|
||||
* @brief Creates a vertex buffer with a specified vertex count and layout.
|
||||
* @param vertexCount The number of vertices in the buffer.
|
||||
* @param vbih A handle to a VertexBufferInfo object describing the vertex layout.
|
||||
* @return A handle to the new vertex buffer.
|
||||
*/
|
||||
|
||||
/** @fn backend::IndexBufferHandle Driver::createIndexBuffer(backend::ElementType elementType, uint32_t indexCount, backend::BufferUsage usage)
|
||||
* @brief Creates an index buffer for indexed drawing.
|
||||
* @param elementType The data type of an index (e.g., USHORT, UINT).
|
||||
* @param indexCount The number of indices in the buffer.
|
||||
* @param usage The expected usage pattern of the buffer (e.g., STATIC, DYNAMIC).
|
||||
* @return A handle to the new index buffer.
|
||||
*/
|
||||
|
||||
/** @fn backend::BufferObjectHandle Driver::createBufferObject(uint32_t byteCount, backend::BufferObjectBinding bindingType, backend::BufferUsage usage)
|
||||
* @brief Creates a generic buffer object for storing arbitrary data on the GPU.
|
||||
* @param byteCount The size of the buffer in bytes.
|
||||
* @param bindingType The purpose of the buffer object (e.g., VERTEX, UNIFORM).
|
||||
* @param usage The expected usage pattern of the buffer.
|
||||
* @return A handle to the new buffer object.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTexture(backend::SamplerType target, uint8_t levels, backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, backend::TextureUsage usage)
|
||||
* @brief Creates a new texture object.
|
||||
* @param target The type of the texture (e.g. 2D, 3D, CUBEMAP).
|
||||
* @param levels The number of mipmap levels.
|
||||
* @param format The internal format of the texture's pixels.
|
||||
* @param samples The number of samples for multisampling (1 for non-MSAA).
|
||||
* @param width The width of the texture in texels.
|
||||
* @param height The height of the texture in texels.
|
||||
* @param depth The depth of the texture in texels (for 3D or array textures).
|
||||
* @param usage A bitmask specifying the intended usage of the texture.
|
||||
* @return A handle to the new texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureView(backend::TextureHandle texture, uint8_t baseLevel, uint8_t levelCount)
|
||||
* @brief Creates a new texture view from an existing texture, allowing access to a subset of its mipmap levels.
|
||||
* @param texture The handle of the original texture.
|
||||
* @param baseLevel The first mipmap level to include in the view.
|
||||
* @param levelCount The number of mipmap levels to include in the view.
|
||||
* @return A handle to the new texture view.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureViewSwizzle(backend::TextureHandle texture, backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, backend::TextureSwizzle a)
|
||||
* @brief Creates a new texture view with remapped color channels (swizzling).
|
||||
* @param texture The handle of the original texture.
|
||||
* @param r The swizzle operation for the red channel.
|
||||
* @param g The swizzle operation for the green channel.
|
||||
* @param b The swizzle operation for the blue channel.
|
||||
* @param a The swizzle operation for the alpha channel.
|
||||
* @return A handle to the new texture view.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureExternalImage2(backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, backend::Platform::ExternalImageHandleRef image)
|
||||
* @brief Creates a texture from a platform-specific external image handle.
|
||||
* @param target The type of the texture.
|
||||
* @param format The format of the texture's pixels.
|
||||
* @param width The width of the texture.
|
||||
* @param height The height of the texture.
|
||||
* @param usage The expected usage of the texture.
|
||||
* @param image A reference to the platform-specific external image.
|
||||
* @return A handle to the new texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureExternalImage(backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image)
|
||||
* @brief Creates a texture from a platform-specific external image pointer.
|
||||
* @param target The type of the texture.
|
||||
* @param format The format of the texture's pixels.
|
||||
* @param width The width of the texture.
|
||||
* @param height The height of the texture.
|
||||
* @param usage The expected usage of the texture.
|
||||
* @param image A pointer to the external image data.
|
||||
* @return A handle to the new texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureExternalImagePlane(backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image, uint32_t plane)
|
||||
* @brief Creates a texture from a single plane of a multi-planar external image.
|
||||
* @param format The format of the texture's pixels.
|
||||
* @param width The width of the texture.
|
||||
* @param height The height of the texture.
|
||||
* @param usage The expected usage of the texture.
|
||||
* @param image A pointer to the external image data.
|
||||
* @param plane The index of the plane to create the texture from.
|
||||
* @return A handle to the new texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::importTexture(intptr_t id, backend::SamplerType target, uint8_t levels, backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, backend::TextureUsage usage)
|
||||
* @brief Imports an existing backend-native texture into Filament.
|
||||
* @param id The backend-specific texture ID or handle.
|
||||
* @param target The type of the texture.
|
||||
* @param levels The number of mipmap levels.
|
||||
* @param format The format of the texture's pixels.
|
||||
* @param samples The number of samples for multisampling.
|
||||
* @param width The width of the texture.
|
||||
* @param height The height of the texture.
|
||||
* @param depth The depth of the texture.
|
||||
* @param usage The expected usage of the texture.
|
||||
* @return A handle to the imported texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::RenderPrimitiveHandle Driver::createRenderPrimitive(backend::VertexBufferHandle vbh, backend::IndexBufferHandle ibh, backend::PrimitiveType pt)
|
||||
* @brief Creates a render primitive, which represents a piece of geometry to be rendered.
|
||||
* @param vbh A handle to a vertex buffer.
|
||||
* @param ibh A handle to an index buffer.
|
||||
* @param pt The type of primitive to render (e.g., TRIANGLES, LINES).
|
||||
* @return A handle to the new render primitive.
|
||||
*/
|
||||
|
||||
/** @fn backend::ProgramHandle Driver::createProgram(backend::Program&& program)
|
||||
* @brief Creates a shader program from a backend-specific program object.
|
||||
* @param program A Program object containing the shader code and metadata.
|
||||
* @return A handle to the new program.
|
||||
*/
|
||||
|
||||
/** @fn backend::RenderTargetHandle Driver::createDefaultRenderTarget()
|
||||
* @brief Creates a render target that represents the default framebuffer (e.g., the screen).
|
||||
* @return A handle to the default render target.
|
||||
*/
|
||||
|
||||
/** @fn backend::RenderTargetHandle Driver::createRenderTarget(backend::TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, backend::MRT color, backend::TargetBufferInfo depth, backend::TargetBufferInfo stencil)
|
||||
* @brief Creates an offscreen render target.
|
||||
* @param targetBufferFlags A bitmask specifying which buffers (color, depth, stencil) are included.
|
||||
* @param width The width of the render target in pixels.
|
||||
* @param height The height of the render target in pixels.
|
||||
* @param samples The number of samples for multisampling.
|
||||
* @param layerCount The number of layers for layered rendering.
|
||||
* @param color An array of color attachments.
|
||||
* @param depth The depth attachment.
|
||||
* @param stencil The stencil attachment.
|
||||
* @return A handle to the new render target.
|
||||
*/
|
||||
|
||||
/** @fn backend::FenceHandle Driver::createFence()
|
||||
* @brief Creates a fence for synchronization between the CPU and GPU.
|
||||
* @return A handle to the new fence.
|
||||
*/
|
||||
|
||||
/** @fn backend::SwapChainHandle Driver::createSwapChain(void* nativeWindow, uint64_t flags)
|
||||
* @brief Creates a swap chain for a native window, used for presenting rendered frames.
|
||||
* @param nativeWindow A platform-specific pointer to the native window.
|
||||
* @param flags Configuration flags for the swap chain.
|
||||
* @return A handle to the new swap chain.
|
||||
*/
|
||||
|
||||
/** @fn backend::SwapChainHandle Driver::createSwapChainHeadless(uint32_t width, uint32_t height, uint64_t flags)
|
||||
* @brief Creates a headless swap chain for offscreen rendering without a native window.
|
||||
* @param width The width of the swap chain.
|
||||
* @param height The height of the swap chain.
|
||||
* @param flags Configuration flags for the swap chain.
|
||||
* @return A handle to the new swap chain.
|
||||
*/
|
||||
|
||||
/** @fn backend::TimerQueryHandle Driver::createTimerQuery()
|
||||
* @brief Creates a timer query object for measuring GPU execution time.
|
||||
* @return A handle to the new timer query.
|
||||
*/
|
||||
|
||||
/** @fn backend::DescriptorSetLayoutHandle Driver::createDescriptorSetLayout(backend::DescriptorSetLayout&& info)
|
||||
* @brief Creates a descriptor set layout, which defines the layout of bindings in a descriptor set.
|
||||
* @param info The layout information for the descriptor set.
|
||||
* @return A handle to the new descriptor set layout.
|
||||
*/
|
||||
|
||||
/** @fn backend::DescriptorSetHandle Driver::createDescriptorSet(backend::DescriptorSetLayoutHandle dslh)
|
||||
* @brief Creates a descriptor set based on a given layout.
|
||||
* @param dslh A handle to the descriptor set layout.
|
||||
* @return A handle to the new descriptor set.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateDescriptorSetBuffer(backend::DescriptorSetHandle dsh, backend::descriptor_binding_t binding, backend::BufferObjectHandle boh, uint32_t offset, uint32_t size)
|
||||
* @brief Updates a buffer descriptor within a descriptor set.
|
||||
* @param dsh The descriptor set to update.
|
||||
* @param binding The binding point of the descriptor.
|
||||
* @param boh The handle of the buffer object to bind.
|
||||
* @param offset The offset into the buffer.
|
||||
* @param size The size of the buffer region to bind.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateDescriptorSetTexture(backend::DescriptorSetHandle dsh, backend::descriptor_binding_t binding, backend::TextureHandle th, SamplerParams params)
|
||||
* @brief Updates a texture descriptor within a descriptor set.
|
||||
* @param dsh The descriptor set to update.
|
||||
* @param binding The binding point of the descriptor.
|
||||
* @param th The handle of the texture to bind.
|
||||
* @param params The sampler parameters for the texture.
|
||||
*/
|
||||
|
||||
/** @fn Driver::bindDescriptorSet(backend::DescriptorSetHandle dsh, backend::descriptor_set_t set, backend::DescriptorSetOffsetArray&& offsets)
|
||||
* @brief Binds a descriptor set to the current pipeline.
|
||||
* @param dsh The descriptor set to bind.
|
||||
* @param set The index of the descriptor set to bind.
|
||||
* @param offsets An array of dynamic offsets for the descriptor set.
|
||||
*/
|
||||
|
||||
// Resource destruction
|
||||
|
||||
/** @fn Driver::destroyVertexBuffer(backend::VertexBufferHandle vbh)
|
||||
* @brief Destroys a vertex buffer and releases its resources.
|
||||
* @param vbh The handle of the vertex buffer to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyVertexBufferInfo(backend::VertexBufferInfoHandle vbih)
|
||||
* @brief Destroys a VertexBufferInfo object.
|
||||
* @param vbih The handle of the VertexBufferInfo object to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyIndexBuffer(backend::IndexBufferHandle ibh)
|
||||
* @brief Destroys an index buffer and releases its resources.
|
||||
* @param ibh The handle of the index buffer to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyBufferObject(backend::BufferObjectHandle boh)
|
||||
* @brief Destroys a buffer object and releases its resources.
|
||||
* @param boh The handle of the buffer object to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyRenderPrimitive(backend::RenderPrimitiveHandle rph)
|
||||
* @brief Destroys a render primitive.
|
||||
* @param rph The handle of the render primitive to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyProgram(backend::ProgramHandle ph)
|
||||
* @brief Destroys a shader program.
|
||||
* @param ph The handle of the program to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyTexture(backend::TextureHandle th)
|
||||
* @brief Destroys a texture and releases its resources.
|
||||
* @param th The handle of the texture to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyRenderTarget(backend::RenderTargetHandle rth)
|
||||
* @brief Destroys a render target.
|
||||
* @param rth The handle of the render target to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroySwapChain(backend::SwapChainHandle sch)
|
||||
* @brief Destroys a swap chain.
|
||||
* @param sch The handle of the swap chain to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyStream(backend::StreamHandle sh)
|
||||
* @brief Destroys a stream.
|
||||
* @param sh The handle of the stream to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyTimerQuery(backend::TimerQueryHandle tqh)
|
||||
* @brief Destroys a timer query object.
|
||||
* @param tqh The handle of the timer query to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyFence(backend::FenceHandle fh)
|
||||
* @brief Destroys a fence.
|
||||
* @param fh The handle of the fence to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyDescriptorSetLayout(backend::DescriptorSetLayoutHandle dslh)
|
||||
* @brief Destroys a descriptor set layout.
|
||||
* @param dslh The handle of the descriptor set layout to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyDescriptorSet(backend::DescriptorSetHandle dsh)
|
||||
* @brief Destroys a descriptor set.
|
||||
* @param dsh The handle of the descriptor set to destroy.
|
||||
*/
|
||||
|
||||
// Synchronous APIs
|
||||
|
||||
/** @fn void Driver::terminate()
|
||||
* @brief Terminates the driver and releases all associated resources. This is a synchronous operation.
|
||||
*/
|
||||
|
||||
/** @fn backend::StreamHandle Driver::createStreamNative(void* stream)
|
||||
* @brief Creates a stream from a native stream object (e.g., a SurfaceTexture on Android).
|
||||
* @param stream A pointer to the native stream object.
|
||||
* @return A handle to the new stream.
|
||||
*/
|
||||
|
||||
/** @fn backend::StreamHandle Driver::createStreamAcquired()
|
||||
* @brief Creates a stream that will be populated with images acquired via `setAcquiredImage`.
|
||||
* @return A handle to the new stream.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::setAcquiredImage(backend::StreamHandle stream, void* image, const math::mat3f& transform, backend::CallbackHandler* handler, backend::StreamCallback cb, void* userData)
|
||||
* @brief Provides an image to an acquired stream, with a callback for when the image is no longer in use.
|
||||
* @param stream The handle of the stream.
|
||||
* @param image A pointer to the image data.
|
||||
* @param transform The transformation matrix for the image.
|
||||
* @param handler The callback handler.
|
||||
* @param cb The callback function to be invoked when the image is released.
|
||||
* @param userData User data for the callback.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::setStreamDimensions(backend::StreamHandle stream, uint32_t width, uint32_t height)
|
||||
* @brief Sets the dimensions of a stream, which may be necessary for certain stream types.
|
||||
* @param stream The handle of the stream.
|
||||
* @param width The new width of the stream.
|
||||
* @param height The new height of the stream.
|
||||
*/
|
||||
|
||||
/** @fn int64_t Driver::getStreamTimestamp(backend::StreamHandle stream)
|
||||
* @brief Gets the timestamp of the last frame from a stream, if available.
|
||||
* @param stream The handle of the stream.
|
||||
* @return The timestamp in nanoseconds, or 0 if not available.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::updateStreams(backend::DriverApi* driver)
|
||||
* @brief Updates all active streams, typically called once per frame.
|
||||
* @param driver The driver API pointer.
|
||||
*/
|
||||
|
||||
/** @fn backend::FenceStatus Driver::getFenceStatus(backend::FenceHandle fh)
|
||||
* @brief Gets the current status of a fence.
|
||||
* @param fh The handle of the fence.
|
||||
* @return The status of the fence (e.g., SIGNALED, TIMEOUT_EXPIRED).
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isTextureFormatSupported(backend::TextureFormat format)
|
||||
* @brief Checks if a specific texture format is supported by the driver.
|
||||
* @param format The texture format to check.
|
||||
* @return True if the format is supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isTextureSwizzleSupported()
|
||||
* @brief Checks if texture channel swizzling is supported by the driver.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isTextureFormatMipmappable(backend::TextureFormat format)
|
||||
* @brief Checks if a texture format can have mipmaps automatically generated.
|
||||
* @param format The texture format to check.
|
||||
* @return True if the format is mipmappable, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isRenderTargetFormatSupported(backend::TextureFormat format)
|
||||
* @brief Checks if a texture format is supported for use as a render target attachment.
|
||||
* @param format The texture format to check.
|
||||
* @return True if the format is supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isFrameBufferFetchSupported()
|
||||
* @brief Checks if framebuffer fetch (reading from the framebuffer in a shader) is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isFrameBufferFetchMultiSampleSupported()
|
||||
* @brief Checks if multisampled framebuffer fetch is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isFrameTimeSupported()
|
||||
* @brief Checks if frame time queries are supported for performance measurement.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isAutoDepthResolveSupported()
|
||||
* @brief Checks if automatic resolution of multisampled depth buffers is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isSRGBSwapChainSupported()
|
||||
* @brief Checks if sRGB swap chains are supported for correct color space handling.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isProtectedContentSupported()
|
||||
* @brief Checks if rendering protected content (e.g., for DRM) is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isStereoSupported()
|
||||
* @brief Checks if stereoscopic rendering is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isParallelShaderCompileSupported()
|
||||
* @brief Checks if the driver can compile shaders in parallel for improved performance.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isDepthStencilResolveSupported()
|
||||
* @brief Checks if resolving multisampled depth/stencil buffers is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isDepthStencilBlitSupported(backend::TextureFormat format)
|
||||
* @brief Checks if blitting (copying) depth/stencil data is supported for a given format.
|
||||
* @param format The texture format.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isProtectedTexturesSupported()
|
||||
* @brief Checks if creating protected textures is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isDepthClampSupported()
|
||||
* @brief Checks if depth clamping is supported by the hardware.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn uint8_t Driver::getMaxDrawBuffers()
|
||||
* @brief Gets the maximum number of simultaneous draw buffers (Multiple Render Targets).
|
||||
* @return The maximum number of draw buffers.
|
||||
*/
|
||||
|
||||
/** @fn size_t Driver::getMaxUniformBufferSize()
|
||||
* @brief Gets the maximum size of a uniform buffer in bytes.
|
||||
* @return The maximum size in bytes.
|
||||
*/
|
||||
|
||||
/** @fn size_t Driver::getMaxTextureSize(backend::SamplerType target)
|
||||
* @brief Gets the maximum texture dimension (width, height, or depth) for a given target type.
|
||||
* @param target The texture target type.
|
||||
* @return The maximum size in texels.
|
||||
*/
|
||||
|
||||
/** @fn size_t Driver::getMaxArrayTextureLayers()
|
||||
* @brief Gets the maximum number of layers in an array texture.
|
||||
* @return The maximum number of layers.
|
||||
*/
|
||||
|
||||
/** @fn math::float2 Driver::getClipSpaceParams()
|
||||
* @brief Gets the clip space parameters for the current backend.
|
||||
* @return A float2 containing clip space parameters.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::setupExternalImage2(backend::Platform::ExternalImageHandleRef image)
|
||||
* @brief Performs any necessary setup for an external image before it can be used.
|
||||
* @param image A reference to the external image.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::setupExternalImage(void* image)
|
||||
* @brief Performs any necessary setup for an external image before it can be used.
|
||||
* @param image A pointer to the external image.
|
||||
*/
|
||||
|
||||
/** @fn backend::TimerQueryResult Driver::getTimerQueryValue(backend::TimerQueryHandle query, uint64_t* elapsedTime)
|
||||
* @brief Gets the result of a timer query, providing the elapsed GPU time.
|
||||
* @param query The timer query handle.
|
||||
* @param elapsedTime A pointer to store the elapsed time in nanoseconds.
|
||||
* @return The result of the query (e.g., AVAILABLE, NOT_READY).
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isWorkaroundNeeded(backend::Workaround workaround)
|
||||
* @brief Checks if a specific driver or hardware workaround is needed.
|
||||
* @param workaround The workaround to check.
|
||||
* @return True if the workaround is needed, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn backend::FeatureLevel Driver::getFeatureLevel()
|
||||
* @brief Gets the feature level supported by the driver.
|
||||
* @return The driver's feature level.
|
||||
*/
|
||||
|
||||
// Resource updates
|
||||
|
||||
/** @fn Driver::setVertexBufferObject(backend::VertexBufferHandle vbh, uint32_t index, backend::BufferObjectHandle bufferObject)
|
||||
* @brief Associates a buffer object with a specific buffer slot in a vertex buffer.
|
||||
* @param vbh The handle of the vertex buffer.
|
||||
* @param index The index of the buffer slot to set.
|
||||
* @param bufferObject The handle of the buffer object to associate.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateIndexBuffer(backend::IndexBufferHandle ibh, backend::BufferDescriptor&& data, uint32_t byteOffset)
|
||||
* @brief Updates the data of an index buffer.
|
||||
* @param ibh The handle of the index buffer.
|
||||
* @param data The new data to upload.
|
||||
* @param byteOffset The offset in bytes into the buffer to start writing to.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateBufferObject(backend::BufferObjectHandle boh, backend::BufferDescriptor&& data, uint32_t byteOffset)
|
||||
* @brief Updates the data of a buffer object.
|
||||
* @param boh The handle of the buffer object.
|
||||
* @param data The new data to upload.
|
||||
* @param byteOffset The offset in bytes into the buffer to start writing to.
|
||||
*/
|
||||
|
||||
/** @fn Driver::registerBufferObjectStreams(backend::BufferObjectHandle boh, backend::BufferObjectStreamDescriptor&& streams)
|
||||
* @brief Registers external streams with a buffer object for efficient data transfer.
|
||||
* @param boh The handle of the buffer object.
|
||||
* @param streams The stream descriptors.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateBufferObjectUnsynchronized(backend::BufferObjectHandle boh, backend::BufferDescriptor&& data, uint32_t byteOffset)
|
||||
* @brief Updates a buffer object without synchronization, for use when manual synchronization is handled.
|
||||
* @param boh The handle of the buffer object.
|
||||
* @param data The new data to upload.
|
||||
* @param byteOffset The offset in bytes into the buffer to start writing to.
|
||||
*/
|
||||
|
||||
/** @fn Driver::resetBufferObject(backend::BufferObjectHandle boh)
|
||||
* @brief Resets a buffer object, potentially discarding its contents.
|
||||
* @param boh The handle of the buffer object.
|
||||
*/
|
||||
|
||||
/** @fn Driver::update3DImage(backend::TextureHandle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, backend::PixelBufferDescriptor&& data)
|
||||
* @brief Updates a sub-region of a 3D texture.
|
||||
* @param th The handle of the texture.
|
||||
* @param level The mipmap level to update.
|
||||
* @param xoffset The x offset of the sub-region.
|
||||
* @param yoffset The y offset of the sub-region.
|
||||
* @param zoffset The z offset of the sub-region.
|
||||
* @param width The width of the sub-region.
|
||||
* @param height The height of the sub-region.
|
||||
* @param depth The depth of the sub-region.
|
||||
* @param data The pixel data to upload.
|
||||
*/
|
||||
|
||||
/** @fn Driver::generateMipmaps(backend::TextureHandle th)
|
||||
* @brief Generates mipmaps for a texture automatically.
|
||||
* @param th The handle of the texture.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setExternalStream(backend::TextureHandle th, backend::StreamHandle sh)
|
||||
* @brief Associates an external stream with a texture for video or camera input.
|
||||
* @param th The handle of the texture.
|
||||
* @param sh The handle of the stream.
|
||||
*/
|
||||
|
||||
// Render passes
|
||||
|
||||
/** @fn Driver::beginRenderPass(backend::RenderTargetHandle rth, const backend::RenderPassParams& params)
|
||||
* @brief Begins a new render pass, targeting a specific render target.
|
||||
* @param rth The handle of the render target.
|
||||
* @param params The parameters for the render pass (e.g., clear values, load/store actions).
|
||||
*/
|
||||
|
||||
/** @fn Driver::endRenderPass()
|
||||
* @brief Ends the current render pass.
|
||||
*/
|
||||
|
||||
/** @fn Driver::nextSubpass()
|
||||
* @brief Moves to the next subpass within the current render pass.
|
||||
*/
|
||||
|
||||
// Timer queries
|
||||
|
||||
/** @fn Driver::beginTimerQuery(backend::TimerQueryHandle query)
|
||||
* @brief Begins a timer query to measure GPU time.
|
||||
* @param query The handle of the timer query.
|
||||
*/
|
||||
|
||||
/** @fn Driver::endTimerQuery(backend::TimerQueryHandle query)
|
||||
* @brief Ends a timer query.
|
||||
* @param query The handle of the timer query.
|
||||
*/
|
||||
|
||||
/** @fn Driver::compilePrograms(backend::CompilerPriorityQueue priority, backend::CallbackHandler* handler, backend::CallbackHandler::Callback callback, void* user)
|
||||
* @brief Compiles a queue of pending shader programs.
|
||||
* @param priority The priority queue to use for compilation.
|
||||
* @param handler The callback handler.
|
||||
* @param callback The callback function to be invoked upon completion.
|
||||
* @param user User data for the callback.
|
||||
*/
|
||||
|
||||
// Swap chain
|
||||
|
||||
/** @fn Driver::makeCurrent(backend::SwapChainHandle schDraw, backend::SwapChainHandle schRead)
|
||||
* @brief Makes a swap chain current for drawing and reading.
|
||||
* @param schDraw The draw swap chain.
|
||||
* @param schRead The read swap chain.
|
||||
*/
|
||||
|
||||
/** @fn Driver::commit(backend::SwapChainHandle sch)
|
||||
* @brief Commits the back buffer of a swap chain, making it visible.
|
||||
* @param sch The handle of the swap chain.
|
||||
*/
|
||||
|
||||
// Rendering state
|
||||
|
||||
/** @fn Driver::setPushConstant(backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant value)
|
||||
* @brief Sets a push constant value for a specific shader stage.
|
||||
* @param stage The shader stage to set the constant for.
|
||||
* @param index The index of the push constant.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
|
||||
/** @fn Driver::insertEventMarker(const char* string)
|
||||
* @brief Inserts a debug event marker into the command stream.
|
||||
* @param string The marker string.
|
||||
*/
|
||||
|
||||
/** @fn Driver::pushGroupMarker(const char* string)
|
||||
* @brief Pushes a debug group marker onto the command stream stack.
|
||||
* @param string The marker string.
|
||||
*/
|
||||
|
||||
/** @fn Driver::popGroupMarker()
|
||||
* @brief Pops a debug group marker from the command stream stack.
|
||||
*/
|
||||
|
||||
/** @fn Driver::startCapture()
|
||||
* @brief Starts a graphics capture session.
|
||||
*/
|
||||
|
||||
/** @fn Driver::stopCapture()
|
||||
* @brief Stops a graphics capture session.
|
||||
*/
|
||||
|
||||
// Read-back
|
||||
|
||||
/** @fn Driver::readPixels(backend::RenderTargetHandle src, uint32_t x, uint32_t y, uint32_t width, uint32_t height, backend::PixelBufferDescriptor&& data)
|
||||
* @brief Reads a block of pixels from a render target into a client-side buffer.
|
||||
* @param src The source render target.
|
||||
* @param x The x coordinate of the region to read.
|
||||
* @param y The y coordinate of the region to read.
|
||||
* @param width The width of the region to read.
|
||||
* @param height The height of the region to read.
|
||||
* @param data The buffer to store the pixel data.
|
||||
*/
|
||||
|
||||
/** @fn Driver::readBufferSubData(backend::BufferObjectHandle src, uint32_t offset, uint32_t size, backend::BufferDescriptor&& data)
|
||||
* @brief Reads a sub-region of a buffer object into a client-side buffer.
|
||||
* @param src The source buffer object.
|
||||
* @param offset The offset to start reading from.
|
||||
* @param size The number of bytes to read.
|
||||
* @param data The buffer to store the data.
|
||||
*/
|
||||
|
||||
// Rendering
|
||||
|
||||
/** @fn Driver::blitDEPRECATED(backend::TargetBufferFlags buffers, backend::RenderTargetHandle dst, backend::Viewport dstRect, backend::RenderTargetHandle src, backend::Viewport srcRect, backend::SamplerMagFilter filter)
|
||||
* @brief Blits (copies) a region from one render target to another (deprecated).
|
||||
* @param buffers The buffers to blit (e.g., color, depth).
|
||||
* @param dst The destination render target.
|
||||
* @param dstRect The destination rectangle.
|
||||
* @param src The source render target.
|
||||
* @param srcRect The source rectangle.
|
||||
* @param filter The filter to use for scaling.
|
||||
*/
|
||||
|
||||
/** @fn Driver::resolve(backend::TextureHandle dst, uint8_t dstLevel, uint8_t dstLayer, backend::TextureHandle src, uint8_t srcLevel, uint8_t srcLayer)
|
||||
* @brief Resolves a multisampled texture into a non-multisampled texture.
|
||||
* @param dst The destination texture.
|
||||
* @param dstLevel The destination mipmap level.
|
||||
* @param dstLayer The destination layer.
|
||||
* @param src The source multisampled texture.
|
||||
* @param srcLevel The source mipmap level.
|
||||
* @param srcLayer The source layer.
|
||||
*/
|
||||
|
||||
/** @fn Driver::blit(backend::TextureHandle dst, uint8_t dstLevel, uint8_t dstLayer, math::uint2 dstOrigin, backend::TextureHandle src, uint8_t srcLevel, uint8_t srcLayer, math::uint2 srcOrigin, math::uint2 size)
|
||||
* @brief Blits (copies) a region from one texture to another.
|
||||
* @param dst The destination texture.
|
||||
* @param dstLevel The destination mipmap level.
|
||||
* @param dstLayer The destination layer.
|
||||
* @param dstOrigin The origin of the destination region.
|
||||
* @param src The source texture.
|
||||
* @param srcLevel The source mipmap level.
|
||||
* @param srcLayer The source layer.
|
||||
* @param srcOrigin The origin of the source region.
|
||||
* @param size The size of the region to blit.
|
||||
*/
|
||||
|
||||
/** @fn Driver::bindPipeline(const backend::PipelineState& state)
|
||||
* @brief Binds a pipeline state object, including shaders and render states.
|
||||
* @param state The pipeline state to bind.
|
||||
*/
|
||||
|
||||
/** @fn Driver::bindRenderPrimitive(backend::RenderPrimitiveHandle rph)
|
||||
* @brief Binds a render primitive for subsequent draw calls.
|
||||
* @param rph The handle of the render primitive.
|
||||
*/
|
||||
|
||||
/** @fn Driver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount)
|
||||
* @brief Draws a render primitive using the currently bound pipeline and primitive.
|
||||
* @param indexOffset The offset into the index buffer.
|
||||
* @param indexCount The number of indices to draw.
|
||||
* @param instanceCount The number of instances to draw.
|
||||
*/
|
||||
|
||||
/** @fn Driver::draw(backend::PipelineState state, backend::RenderPrimitiveHandle rph, uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount)
|
||||
* @brief A combined call to bind a pipeline, bind a primitive, and draw.
|
||||
* @param state The pipeline state to bind.
|
||||
* @param rph The render primitive to bind.
|
||||
* @param indexOffset The offset into the index buffer.
|
||||
* @param indexCount The number of indices to draw.
|
||||
* @param instanceCount The number of instances to draw.
|
||||
*/
|
||||
|
||||
/** @fn Driver::dispatchCompute(backend::ProgramHandle program, math::uint3 workGroupCount)
|
||||
* @brief Dispatches a compute shader.
|
||||
* @param program The handle of the compute program.
|
||||
* @param workGroupCount The number of work groups to dispatch in each dimension.
|
||||
*/
|
||||
|
||||
/** @fn Driver::scissor(Viewport scissor)
|
||||
* @brief Sets the scissor rectangle for clipping.
|
||||
* @param scissor The scissor rectangle.
|
||||
*/
|
||||
@@ -156,27 +156,27 @@ void VulkanStagePool::destroyStage(VulkanStage const*&& stage) {
|
||||
delete stage;
|
||||
}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanStageImage::Resource> VulkanStagePool::acquireImage(
|
||||
PixelDataFormat format, PixelDataType type, uint32_t width, uint32_t height) {
|
||||
// Helper lambda so we can return stage images wrapped as resources that can
|
||||
// be held by command buffers until no longer needed.
|
||||
auto wrapAsResource = [this](VulkanStageImage* image) {
|
||||
auto recycleFn = [this](VulkanStageImage* image) {
|
||||
this->mFreeImages.insert(image);
|
||||
};
|
||||
return fvkmemory::resource_ptr<VulkanStageImage::Resource>::construct(
|
||||
this->mResManager, image, recycleFn);
|
||||
};
|
||||
|
||||
VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, PixelDataType type,
|
||||
uint32_t width, uint32_t height) {
|
||||
const VkFormat vkformat = fvkutils::getVkFormat(format, type);
|
||||
for (auto stageImage : mFreeImages) {
|
||||
if (stageImage->format() == vkformat && stageImage->width() == width && stageImage->height() == height) {
|
||||
mFreeImages.erase(stageImage);
|
||||
stageImage->mLastAccessed = mCurrentFrame;
|
||||
return wrapAsResource(stageImage);
|
||||
for (auto image : mFreeImages) {
|
||||
if (image->format == vkformat && image->width == width && image->height == height) {
|
||||
mFreeImages.erase(image);
|
||||
image->lastAccessed = mCurrentFrame;
|
||||
mUsedImages.push_back(image);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
||||
VulkanStageImage* image = new VulkanStageImage({
|
||||
.format = vkformat,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.lastAccessed = mCurrentFrame,
|
||||
});
|
||||
|
||||
mUsedImages.push_back(image);
|
||||
|
||||
const VkImageCreateInfo imageInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
@@ -194,19 +194,14 @@ fvkmemory::resource_ptr<VulkanStageImage::Resource> VulkanStagePool::acquireImag
|
||||
.usage = VMA_MEMORY_USAGE_CPU_TO_GPU
|
||||
};
|
||||
|
||||
VkImage image;
|
||||
VmaAllocation memory;
|
||||
const UTILS_UNUSED VkResult result = vmaCreateImage(mAllocator, &imageInfo, &allocInfo,
|
||||
&image, &memory, nullptr);
|
||||
&image->image, &image->memory, nullptr);
|
||||
|
||||
assert_invariant(result == VK_SUCCESS);
|
||||
|
||||
VkImageAspectFlags const aspectFlags = fvkutils::getImageAspect(vkformat);
|
||||
VkCommandBuffer const cmdbuffer = mCommands->get().buffer();
|
||||
|
||||
VulkanStageImage* stageImage = new VulkanStageImage(
|
||||
vkformat, width, height, memory, image, mCurrentFrame);
|
||||
|
||||
// We use VK_IMAGE_LAYOUT_GENERAL here because the spec says:
|
||||
// "Host access to image memory is only well-defined for linear images and for image
|
||||
// subresources of those images which are currently in either the
|
||||
@@ -214,13 +209,12 @@ fvkmemory::resource_ptr<VulkanStageImage::Resource> VulkanStagePool::acquireImag
|
||||
// vkGetImageSubresourceLayout for a linear image returns a subresource layout mapping that is
|
||||
// valid for either of those image layouts."
|
||||
fvkutils::transitionLayout(cmdbuffer, {
|
||||
.image = stageImage->image(),
|
||||
.image = image->image,
|
||||
.oldLayout = VulkanLayout::UNDEFINED,
|
||||
.newLayout = VulkanLayout::STAGING, // (= VK_IMAGE_LAYOUT_GENERAL)
|
||||
.subresources = { aspectFlags, 0, 1, 0, 1 },
|
||||
});
|
||||
|
||||
return wrapAsResource(stageImage);
|
||||
return image;
|
||||
}
|
||||
|
||||
void VulkanStagePool::gc() noexcept {
|
||||
@@ -268,14 +262,25 @@ void VulkanStagePool::gc() noexcept {
|
||||
decltype(mFreeImages) freeImages;
|
||||
freeImages.swap(mFreeImages);
|
||||
for (auto image : freeImages) {
|
||||
if (image->mLastAccessed < evictionTime) {
|
||||
vmaDestroyImage(mAllocator, image->image(), image->memory());
|
||||
if (image->lastAccessed < evictionTime) {
|
||||
vmaDestroyImage(mAllocator, image->image, image->memory);
|
||||
delete image;
|
||||
} else {
|
||||
mFreeImages.insert(image);
|
||||
}
|
||||
}
|
||||
|
||||
// Reclaim images that are no longer being used by any command buffer.
|
||||
decltype(mUsedImages) usedImages;
|
||||
usedImages.swap(mUsedImages);
|
||||
for (auto image : usedImages) {
|
||||
if (image->lastAccessed < evictionTime) {
|
||||
image->lastAccessed = mCurrentFrame;
|
||||
mFreeImages.insert(image);
|
||||
} else {
|
||||
mUsedImages.push_back(image);
|
||||
}
|
||||
}
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
@@ -285,8 +290,14 @@ void VulkanStagePool::terminate() noexcept {
|
||||
}
|
||||
mStages.clear();
|
||||
|
||||
for (auto image : mUsedImages) {
|
||||
vmaDestroyImage(mAllocator, image->image, image->memory);
|
||||
delete image;
|
||||
}
|
||||
mUsedImages.clear();
|
||||
|
||||
for (auto image : mFreeImages) {
|
||||
vmaDestroyImage(mAllocator, image->image(), image->memory());
|
||||
vmaDestroyImage(mAllocator, image->image, image->memory);
|
||||
delete image;
|
||||
}
|
||||
mFreeImages.clear();
|
||||
|
||||
@@ -126,70 +126,13 @@ private:
|
||||
std::unordered_map<uint32_t, Segment*> mSegments;
|
||||
};
|
||||
|
||||
class VulkanStageImage {
|
||||
public:
|
||||
class Resource : public fvkmemory::Resource {
|
||||
public:
|
||||
using RecycleFn = std::function<void(VulkanStageImage*)>;
|
||||
|
||||
Resource(VulkanStageImage* image, RecycleFn&& onRecycleFn)
|
||||
: mImage(image),
|
||||
mOnRecycleFn(onRecycleFn)
|
||||
{}
|
||||
|
||||
~Resource() {
|
||||
if (mOnRecycleFn) {
|
||||
mOnRecycleFn(mImage);
|
||||
}
|
||||
}
|
||||
|
||||
inline VkFormat format() const { return mImage->format(); }
|
||||
inline uint32_t width() const { return mImage->width(); }
|
||||
inline uint32_t height() const { return mImage->height(); }
|
||||
inline VmaAllocation memory() const { return mImage->memory(); }
|
||||
inline VkImage image() const { return mImage->image(); }
|
||||
|
||||
private:
|
||||
Resource() = delete;
|
||||
Resource(const Resource& other) = delete;
|
||||
Resource(Resource&& other) = delete;
|
||||
Resource& operator=(const Resource& other) = delete;
|
||||
Resource& operator=(Resource&& other) = delete;
|
||||
|
||||
VulkanStageImage* const mImage;
|
||||
RecycleFn mOnRecycleFn;
|
||||
};
|
||||
|
||||
VulkanStageImage(VkFormat format, uint32_t width, uint32_t height, VmaAllocation memory,
|
||||
VkImage image, uint64_t lastAccessed)
|
||||
: mFormat(format),
|
||||
mWidth(width),
|
||||
mHeight(height),
|
||||
mMemory(memory),
|
||||
mImage(image),
|
||||
mLastAccessed(lastAccessed) {}
|
||||
|
||||
VulkanStageImage(const VulkanStageImage& other) = delete;
|
||||
VulkanStageImage(VulkanStageImage&& other) = delete;
|
||||
VulkanStageImage& operator=(const VulkanStageImage& other) = delete;
|
||||
VulkanStageImage& operator=(VulkanStageImage&& other) = delete;
|
||||
|
||||
inline VkFormat format() const { return mFormat; }
|
||||
inline uint32_t width() const { return mWidth; }
|
||||
inline uint32_t height() const { return mHeight; }
|
||||
inline VmaAllocation memory() const { return mMemory; }
|
||||
inline VkImage image() const { return mImage; }
|
||||
|
||||
private:
|
||||
const VkFormat mFormat;
|
||||
const uint32_t mWidth;
|
||||
const uint32_t mHeight;
|
||||
const VmaAllocation mMemory;
|
||||
const VkImage mImage;
|
||||
|
||||
uint64_t mLastAccessed;
|
||||
// Denote as a friend so that it can update mLastAccessed.
|
||||
friend class VulkanStagePool;
|
||||
struct VulkanStageImage {
|
||||
VkFormat format;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
mutable uint64_t lastAccessed;
|
||||
VmaAllocation memory;
|
||||
VkImage image;
|
||||
};
|
||||
|
||||
// Manages a pool of stages, periodically releasing stages that have been unused for a while.
|
||||
@@ -210,7 +153,7 @@ public:
|
||||
uint32_t alignment = 0);
|
||||
|
||||
// Images have VK_IMAGE_LAYOUT_GENERAL and must not be transitioned to any other layout
|
||||
fvkmemory::resource_ptr<VulkanStageImage::Resource> acquireImage(PixelDataFormat format, PixelDataType type,
|
||||
VulkanStageImage const* acquireImage(PixelDataFormat format, PixelDataType type,
|
||||
uint32_t width, uint32_t height);
|
||||
|
||||
// Evicts old unused stages and bumps the current frame number.
|
||||
@@ -250,7 +193,8 @@ private:
|
||||
// Use an ordered multimap for quick (capacity => stage) lookups using lower_bound().
|
||||
std::multimap<uint32_t, VulkanStage*> mStages;
|
||||
|
||||
std::unordered_set<VulkanStageImage*> mFreeImages;
|
||||
std::unordered_set<VulkanStageImage const*> mFreeImages;
|
||||
std::vector<VulkanStageImage const*> mUsedImages;
|
||||
|
||||
// Store the current "time" (really just a frame count) and LRU eviction parameters.
|
||||
uint64_t mCurrentFrame = 0;
|
||||
|
||||
@@ -602,16 +602,15 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& data, uint3
|
||||
size_t const writeSize = bpp > 0 ? width * height * depth * bpp : data.size;
|
||||
|
||||
void* mapped = nullptr;
|
||||
fvkmemory::resource_ptr<VulkanStageImage::Resource> stage
|
||||
VulkanStageImage const* stage
|
||||
= mState->mStagePool.acquireImage(data.format, data.type, width, height);
|
||||
vmaMapMemory(mState->mAllocator, stage->memory(), &mapped);
|
||||
vmaMapMemory(mState->mAllocator, stage->memory, &mapped);
|
||||
adjustedMemcpy(mapped, data, width, height, depth);
|
||||
vmaUnmapMemory(mState->mAllocator, stage->memory());
|
||||
vmaFlushAllocation(mState->mAllocator, stage->memory(), 0, writeSize);
|
||||
vmaUnmapMemory(mState->mAllocator, stage->memory);
|
||||
vmaFlushAllocation(mState->mAllocator, stage->memory, 0, writeSize);
|
||||
|
||||
VulkanCommandBuffer& commands = mState->mCommands->get();
|
||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||
commands.acquire(stage);
|
||||
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
|
||||
|
||||
// TODO: support blit-based format conversion for 3D images and cubemaps.
|
||||
@@ -634,7 +633,7 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& data, uint3
|
||||
VulkanLayout const oldLayout = getLayout(layer, miplevel);
|
||||
transitionLayout(&commands, range, newLayout);
|
||||
|
||||
vkCmdBlitImage(cmdbuf, stage->image(), fvkutils::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
vkCmdBlitImage(cmdbuf, stage->image, fvkutils::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
mState->mTextureImage, fvkutils::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
|
||||
|
||||
transitionLayout(&commands, range, oldLayout);
|
||||
|
||||
@@ -27,7 +27,6 @@ template ResourceType getTypeEnum<VulkanProgram>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanRenderTarget>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanSwapChain>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanStage::Segment>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanStageImage::Resource>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanRenderPrimitive>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanTexture>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanTextureState>() noexcept;
|
||||
@@ -59,9 +58,6 @@ ResourceType getTypeEnum() noexcept {
|
||||
if constexpr (std::is_same_v<D, VulkanStage::Segment>) {
|
||||
return ResourceType::STAGE_SEGMENT;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanStageImage::Resource>) {
|
||||
return ResourceType::STAGE_IMAGE;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanRenderPrimitive>) {
|
||||
return ResourceType::RENDER_PRIMITIVE;
|
||||
}
|
||||
@@ -109,8 +105,6 @@ std::string getTypeStr(ResourceType type) {
|
||||
return "SwapChain";
|
||||
case ResourceType::STAGE_SEGMENT:
|
||||
return "Stage::Segment";
|
||||
case ResourceType::STAGE_IMAGE:
|
||||
return "Stage::Image";
|
||||
case ResourceType::RENDER_PRIMITIVE:
|
||||
return "RenderPrimitive";
|
||||
case ResourceType::TEXTURE:
|
||||
|
||||
@@ -51,8 +51,7 @@ enum class ResourceType : uint8_t {
|
||||
FENCE = 13,
|
||||
VULKAN_BUFFER = 14,
|
||||
STAGE_SEGMENT = 15,
|
||||
STAGE_IMAGE = 16,
|
||||
UNDEFINED_TYPE = 17, // Must be the last enum because we use it for iterating over the enums.
|
||||
UNDEFINED_TYPE = 16, // Must be the last enum because we use it for iterating over the enums.
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
|
||||
@@ -81,9 +81,6 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
|
||||
case ResourceType::STAGE_SEGMENT:
|
||||
destruct<VulkanStage::Segment>(Handle<VulkanStage::Segment>(id));
|
||||
break;
|
||||
case ResourceType::STAGE_IMAGE:
|
||||
destruct<VulkanStageImage::Resource>(Handle<VulkanStageImage::Resource>(id));
|
||||
break;
|
||||
case ResourceType::RENDER_PRIMITIVE:
|
||||
destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
|
||||
break;
|
||||
|
||||
@@ -591,7 +591,7 @@ bool WebGPUDriver::isParallelShaderCompileSupported() {
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isDepthStencilResolveSupported() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isDepthStencilBlitSupported(const TextureFormat format) {
|
||||
@@ -1215,6 +1215,12 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We expected this to be a sane check, however it complains when running shadowtest.
|
||||
//if (program->fragmentShaderModule != nullptr) {
|
||||
// FILAMENT_CHECK_POSTCONDITION(!pipelineColorFormats.empty())
|
||||
// << "Render pipeline with fragment shader must have at least one color target "
|
||||
// "format.";
|
||||
//}
|
||||
wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program, *vertexBufferInfo,
|
||||
layout, pipelineState.rasterState, pipelineState.stencilState,
|
||||
pipelineState.polygonOffset, pipelineState.primitiveType, pipelineColorFormats,
|
||||
|
||||
@@ -281,32 +281,27 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
}
|
||||
};
|
||||
if (program.fragmentShaderModule != nullptr) {
|
||||
// According to the WebGPU spec, a pipeline cannot have a fragment stage with zero color
|
||||
// targets. This situation can arise in Filament during depth-only passes (like shadow map
|
||||
// generation) if the material variant still includes a fragment shader.
|
||||
//
|
||||
// To handle this, we check if any color targets are configured for this pipeline. If not, we
|
||||
// create a pipeline *without* a fragment stage. This makes the pipeline valid for a
|
||||
// depth-only pass, allowing depth writes to proceed correctly.
|
||||
if (!colorFormats.empty()) {
|
||||
fragmentState.module = program.fragmentShaderModule;
|
||||
fragmentState.entryPoint = "main";
|
||||
// see the comment about constants for the vertex state, as the same reasoning applies
|
||||
// here
|
||||
fragmentState.constantCount = 0,
|
||||
fragmentState.constants = nullptr,
|
||||
fragmentState.targetCount = colorFormats.size();
|
||||
fragmentState.targets = colorTargets.data();
|
||||
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
|
||||
auto& colorTarget = colorTargets[targetIndex];
|
||||
colorTarget.format = colorFormats[targetIndex];
|
||||
colorTarget.blend = rasterState.hasBlending() ? &blendState : nullptr;
|
||||
colorTarget.writeMask =
|
||||
rasterState.colorWrite ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None;
|
||||
}
|
||||
pipelineDescriptor.fragment = &fragmentState;
|
||||
fragmentState.module = program.fragmentShaderModule;
|
||||
fragmentState.entryPoint = "main";
|
||||
// see the comment about constants for the vertex state, as the same reasoning applies
|
||||
// here
|
||||
fragmentState.constantCount = 0,
|
||||
fragmentState.constants = nullptr,
|
||||
fragmentState.targetCount = colorFormats.size();
|
||||
fragmentState.targets = colorTargets.data();
|
||||
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
// We expect a fragment shader implies at least one color target if it outputs color.
|
||||
// This should be guaranteed by the caller ensuring colorFormats is not empty.
|
||||
// However, this fails on shadowtest.cpp, TODO investigate why
|
||||
// assert_invariant(fragmentState.targetCount > 0);
|
||||
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
|
||||
auto& colorTarget = colorTargets[targetIndex];
|
||||
colorTarget.format = colorFormats[targetIndex];
|
||||
colorTarget.blend = rasterState.hasBlending() ? &blendState : nullptr;
|
||||
colorTarget.writeMask =
|
||||
rasterState.colorWrite ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None;
|
||||
}
|
||||
pipelineDescriptor.fragment = &fragmentState;
|
||||
}
|
||||
const wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(pipeline)
|
||||
|
||||
@@ -118,6 +118,7 @@ INPUT = ../libs/filabridge/include \
|
||||
../libs/gltfio/include \
|
||||
../libs/utils/include \
|
||||
backend/include \
|
||||
backend/include/private/backend/DriverAPI.dox \
|
||||
include
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
|
||||
@@ -82,6 +82,8 @@ RenderPassBuilder& RenderPassBuilder::customCommand(
|
||||
|
||||
RenderPass RenderPassBuilder::build(FEngine const& engine, DriverApi& driver) const {
|
||||
assert_invariant(mRenderableSoa);
|
||||
assert_invariant(mScissorViewport.width <= std::numeric_limits<int32_t>::max());
|
||||
assert_invariant(mScissorViewport.height <= std::numeric_limits<int32_t>::max());
|
||||
return RenderPass{ engine, driver, *this };
|
||||
}
|
||||
|
||||
@@ -105,7 +107,8 @@ void RenderPass::DescriptorSetHandleDeleter::operator()(
|
||||
RenderPass::RenderPass(FEngine const& engine, DriverApi& driver,
|
||||
RenderPassBuilder const& builder) noexcept
|
||||
: mRenderableSoa(*builder.mRenderableSoa),
|
||||
mColorPassDescriptorSet(builder.mColorPassDescriptorSet) {
|
||||
mColorPassDescriptorSet(builder.mColorPassDescriptorSet),
|
||||
mScissorViewport(builder.mScissorViewport) {
|
||||
|
||||
// compute the number of commands we need
|
||||
updateSummedPrimitiveCounts(
|
||||
|
||||
@@ -306,12 +306,6 @@ public:
|
||||
// allocated commands ARE NOT freed, they're owned by the Arena
|
||||
~RenderPass() noexcept;
|
||||
|
||||
// Specifies the viewport for the scissor rectangle, that is, the final scissor rect is
|
||||
// offset by the viewport's left-top and clipped to the viewport's width/height.
|
||||
void setScissorViewport(backend::Viewport const viewport) noexcept {
|
||||
mScissorViewport = viewport;
|
||||
}
|
||||
|
||||
Command const* begin() const noexcept { return mCommandBegin; }
|
||||
Command const* end() const noexcept { return mCommandEnd; }
|
||||
bool empty() const noexcept { return begin() == end(); }
|
||||
@@ -467,7 +461,7 @@ private:
|
||||
|
||||
FScene::RenderableSoa const& mRenderableSoa;
|
||||
ColorPassDescriptorSet const* const mColorPassDescriptorSet;
|
||||
backend::Viewport mScissorViewport{ 0, 0, INT32_MAX, INT32_MAX };
|
||||
backend::Viewport const mScissorViewport{ 0, 0, INT32_MAX, INT32_MAX };
|
||||
Command const* /* const */ mCommandBegin = nullptr; // Pointer to the first command
|
||||
Command const* /* const */ mCommandEnd = nullptr; // Pointer to one past the last command
|
||||
mutable BufferObjectSharedHandle mInstancedUboHandle; // ubo for instanced primitives
|
||||
@@ -482,6 +476,7 @@ class RenderPassBuilder {
|
||||
|
||||
RenderPass::Arena& mArena;
|
||||
RenderPass::CommandTypeFlags mCommandTypeFlags{};
|
||||
backend::Viewport mScissorViewport{ 0, 0, INT32_MAX, INT32_MAX };
|
||||
FScene::RenderableSoa const* mRenderableSoa = nullptr;
|
||||
utils::Range<uint32_t> mVisibleRenderables{};
|
||||
math::float3 mCameraPosition{};
|
||||
@@ -512,6 +507,13 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Specifies the viewport for the scissor rectangle, that is, the final scissor rect is
|
||||
// offset by the viewport's left-top and clipped to the viewport's width/height.
|
||||
RenderPassBuilder& scissorViewport(backend::Viewport const viewport) noexcept {
|
||||
mScissorViewport = viewport;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// specifies the geometry to generate commands for
|
||||
RenderPassBuilder& geometry(
|
||||
FScene::RenderableSoa const& soa, utils::Range<uint32_t> const vr) noexcept {
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -269,94 +270,87 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
RenderPass::Command const* RendererUtils::getFirstRefractionCommand(
|
||||
RenderPass const& pass) noexcept {
|
||||
|
||||
// find the first refractive object in channel 2
|
||||
RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(),
|
||||
[](auto const& command) {
|
||||
constexpr uint64_t mask = RenderPass::CHANNEL_MASK | RenderPass::PASS_MASK;
|
||||
constexpr uint64_t channel = uint64_t(RenderableManager::Builder::DEFAULT_CHANNEL) << RenderPass::CHANNEL_SHIFT;
|
||||
constexpr uint64_t value = channel | uint64_t(RenderPass::Pass::REFRACT);
|
||||
return (command.key & mask) < value;
|
||||
});
|
||||
|
||||
const bool hasScreenSpaceRefraction =
|
||||
(refraction->key & RenderPass::PASS_MASK) == uint64_t(RenderPass::Pass::REFRACT);
|
||||
|
||||
return hasScreenSpaceRefraction ? refraction : nullptr;
|
||||
|
||||
}
|
||||
|
||||
RendererUtils::ColorPassOutput RendererUtils::refractionPass(
|
||||
std::optional<RendererUtils::ColorPassOutput> RendererUtils::refractionPass(
|
||||
FrameGraph& fg, FEngine& engine, FView const& view,
|
||||
ColorPassInput colorPassInput,
|
||||
ColorPassConfig config,
|
||||
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
|
||||
PostProcessManager::ColorGradingConfig const colorGradingConfig,
|
||||
RenderPass const& pass, RenderPass::Command const* const firstRefractionCommand) noexcept {
|
||||
RenderPass const& pass) noexcept {
|
||||
|
||||
assert_invariant(firstRefractionCommand);
|
||||
RenderPass::Command const* const refraction = firstRefractionCommand;
|
||||
// find the first refractive object in channel 2
|
||||
RenderPass::Command const* const refraction = std::partition_point(pass.begin(), pass.end(),
|
||||
[](auto const& command) {
|
||||
constexpr uint64_t mask = RenderPass::CHANNEL_MASK | RenderPass::PASS_MASK;
|
||||
constexpr uint64_t channel = uint64_t(RenderableManager::Builder::DEFAULT_CHANNEL) << RenderPass::CHANNEL_SHIFT;
|
||||
constexpr uint64_t value = channel | uint64_t(RenderPass::Pass::REFRACT);
|
||||
return (command.key & mask) < value;
|
||||
});
|
||||
|
||||
const bool hasScreenSpaceRefraction =
|
||||
(refraction->key & RenderPass::PASS_MASK) == uint64_t(RenderPass::Pass::REFRACT);
|
||||
|
||||
// if there wasn't any refractive object, just skip everything below.
|
||||
assert_invariant(!colorPassInput.linearColor);
|
||||
assert_invariant(!colorPassInput.depth);
|
||||
config.hasScreenSpaceReflectionsOrRefractions = true;
|
||||
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
|
||||
assert_invariant(!colorPassInput.linearColor);
|
||||
assert_invariant(!colorPassInput.depth);
|
||||
config.hasScreenSpaceReflectionsOrRefractions = true;
|
||||
|
||||
PostProcessManager& ppm = engine.getPostProcessManager();
|
||||
auto const opaquePassOutput = colorPass(fg,
|
||||
"Color Pass (opaque)", engine, view, colorPassInput, {
|
||||
// When rendering the opaques, we need to conserve the sample buffer,
|
||||
// so create a config that specifies the sample count.
|
||||
.width = config.physicalViewport.width,
|
||||
.height = config.physicalViewport.height,
|
||||
.samples = config.msaa,
|
||||
.format = config.hdrFormat
|
||||
},
|
||||
config, { .asSubpass = false, .customResolve = false },
|
||||
pass.getExecutor(pass.begin(), refraction));
|
||||
PostProcessManager& ppm = engine.getPostProcessManager();
|
||||
auto const opaquePassOutput = colorPass(fg,
|
||||
"Color Pass (opaque)", engine, view, colorPassInput, {
|
||||
// When rendering the opaques, we need to conserve the sample buffer,
|
||||
// so create a config that specifies the sample count.
|
||||
.width = config.physicalViewport.width,
|
||||
.height = config.physicalViewport.height,
|
||||
.samples = config.msaa,
|
||||
.format = config.hdrFormat
|
||||
},
|
||||
config, { .asSubpass = false, .customResolve = false },
|
||||
pass.getExecutor(pass.begin(), refraction));
|
||||
|
||||
|
||||
// Generate the mipmap chain
|
||||
// Note: we can run some post-processing effects while the "color pass" descriptor set
|
||||
// in bound because only the descriptor 0 (frame uniforms) matters, and it's
|
||||
// present in both.
|
||||
PostProcessManager::generateMipmapSSR(ppm, fg,
|
||||
opaquePassOutput.linearColor,
|
||||
ssrConfig.refraction,
|
||||
true, ssrConfig);
|
||||
// Generate the mipmap chain
|
||||
// Note: we can run some post-processing effects while the "color pass" descriptor set
|
||||
// in bound because only the descriptor 0 (frame uniforms) matters, and it's
|
||||
// present in both.
|
||||
PostProcessManager::generateMipmapSSR(ppm, fg,
|
||||
opaquePassOutput.linearColor,
|
||||
ssrConfig.refraction,
|
||||
true, ssrConfig);
|
||||
|
||||
// Now we're doing the refraction pass proper.
|
||||
// This uses the same framebuffer (color and depth) used by the opaque pass.
|
||||
// For this reason, the `colorBufferDesc` parameter of colorPass() below is only used for
|
||||
// the width and height.
|
||||
colorPassInput.linearColor = opaquePassOutput.linearColor;
|
||||
colorPassInput.depth = opaquePassOutput.depth;
|
||||
// Now we're doing the refraction pass proper.
|
||||
// This uses the same framebuffer (color and depth) used by the opaque pass.
|
||||
// For this reason, the `colorBufferDesc` parameter of colorPass() below is only used for
|
||||
// the width and height.
|
||||
colorPassInput.linearColor = opaquePassOutput.linearColor;
|
||||
colorPassInput.depth = opaquePassOutput.depth;
|
||||
|
||||
// Since we're reusing the existing target we don't want to clear any of its buffer.
|
||||
// Important: if this target ended up being an imported target, then the clearFlags
|
||||
// specified here wouldn't apply (the clearFlags of the imported target take precedence),
|
||||
// and we'd end up clearing the opaque pass. This scenario never happens because it is
|
||||
// prevented in Renderer.cpp's final blit.
|
||||
config.clearFlags = TargetBufferFlags::NONE;
|
||||
auto transparentPassOutput = colorPass(fg, "Color Pass (transparent)",
|
||||
engine, view, colorPassInput, {
|
||||
.width = config.physicalViewport.width,
|
||||
.height = config.physicalViewport.height },
|
||||
config, colorGradingConfig,
|
||||
pass.getExecutor(refraction, pass.end()));
|
||||
// Since we're reusing the existing target we don't want to clear any of its buffer.
|
||||
// Important: if this target ended up being an imported target, then the clearFlags
|
||||
// specified here wouldn't apply (the clearFlags of the imported target take precedence),
|
||||
// and we'd end up clearing the opaque pass. This scenario never happens because it is
|
||||
// prevented in Renderer.cpp's final blit.
|
||||
config.clearFlags = TargetBufferFlags::NONE;
|
||||
auto transparentPassOutput = colorPass(fg, "Color Pass (transparent)",
|
||||
engine, view, colorPassInput, {
|
||||
.width = config.physicalViewport.width,
|
||||
.height = config.physicalViewport.height },
|
||||
config, colorGradingConfig,
|
||||
pass.getExecutor(refraction, pass.end()));
|
||||
|
||||
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
|
||||
// We need to do a resolve here because later passes (such as color grading or DoF) will
|
||||
// need to sample from 'output'. However, because we have MSAA, we know we're not
|
||||
// sampleable. And this is because in the SSR case, we had to use a renderbuffer to
|
||||
// conserve the multi-sample buffer.
|
||||
transparentPassOutput.linearColor = ppm.resolve(fg, "Resolved Color Buffer",
|
||||
transparentPassOutput.linearColor, { .levels = 1 });
|
||||
if (config.msaa > 1 && !colorGradingConfig.asSubpass) {
|
||||
// We need to do a resolve here because later passes (such as color grading or DoF) will
|
||||
// need to sample from 'output'. However, because we have MSAA, we know we're not
|
||||
// sampleable. And this is because in the SSR case, we had to use a renderbuffer to
|
||||
// conserve the multi-sample buffer.
|
||||
transparentPassOutput.linearColor = ppm.resolve(fg, "Resolved Color Buffer",
|
||||
transparentPassOutput.linearColor, { .levels = 1 });
|
||||
}
|
||||
return transparentPassOutput;
|
||||
}
|
||||
return transparentPassOutput;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
|
||||
@@ -26,18 +26,17 @@
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace backend {
|
||||
class PixelBufferDescriptor;
|
||||
}
|
||||
namespace filament {
|
||||
|
||||
class FRenderTarget;
|
||||
class FrameGraph;
|
||||
@@ -100,20 +99,18 @@ public:
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass::Executor passExecutor) noexcept;
|
||||
|
||||
static ColorPassOutput refractionPass(
|
||||
static std::optional<ColorPassOutput> refractionPass(
|
||||
FrameGraph& fg, FEngine& engine, FView const& view,
|
||||
ColorPassInput colorPassInput,
|
||||
ColorPassConfig config,
|
||||
PostProcessManager::ScreenSpaceRefConfig const& ssrConfig,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass const& pass, RenderPass::Command const* firstRefractionCommand) noexcept;
|
||||
RenderPass const& pass) noexcept;
|
||||
|
||||
static void readPixels(backend::DriverApi& driver,
|
||||
backend::Handle<backend::HwRenderTarget> renderTargetHandle,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
backend::PixelBufferDescriptor&& buffer);
|
||||
|
||||
static RenderPass::Command const* getFirstRefractionCommand(RenderPass const& pass) noexcept;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -1125,6 +1125,16 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Color passes
|
||||
|
||||
// this makes the viewport relative to xvp
|
||||
// FIXME: we should use 'vp' when rendering directly into the swapchain, but that's hard to
|
||||
// know at this point. This will usually be the case when post-process is disabled.
|
||||
// FIXME: we probably should take the dynamic scaling into account too
|
||||
// if MSAA is enabled, we end-up rendering in an intermediate buffer. This is the only case where
|
||||
// "!hasPostProcess" doesn't guarantee rendering into the swapchain.
|
||||
const bool useIntermediateBuffer = hasPostProcess || msaaOptions.enabled ||
|
||||
(isRenderingMultiview && engine.debug.stereo.combine_multiview_images);
|
||||
passBuilder.scissorViewport(useIntermediateBuffer ? xvp : vp);
|
||||
|
||||
// This one doesn't need to be a FrameGraph pass because it always happens by construction
|
||||
// (i.e. it won't be culled, unless everything is culled), so no need to complexify things.
|
||||
passBuilder.variant(variant);
|
||||
@@ -1164,35 +1174,8 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
passBuilder.renderFlags(renderFlags);
|
||||
}
|
||||
|
||||
// create the pass, which generates all its commands (this is a heavy operation)
|
||||
RenderPass const pass{ passBuilder.build(engine, driver) };
|
||||
|
||||
// now that we have the commands we can figure out if we have refraction commands
|
||||
auto* const firstRefractionCommand = [&view](RenderPass const& pass) {
|
||||
RenderPass::Command const* p = nullptr;
|
||||
if (UTILS_UNLIKELY(view.isScreenSpaceRefractionEnabled() && !pass.empty())) {
|
||||
p = RendererUtils::getFirstRefractionCommand(pass);
|
||||
}
|
||||
return p;
|
||||
}(pass);
|
||||
|
||||
hasScreenSpaceRefraction = firstRefractionCommand != nullptr;
|
||||
|
||||
// this makes the viewport relative to xvp
|
||||
// FIXME: we should use 'vp' when rendering directly into the swapchain, but that's hard to
|
||||
// know at this point. This will usually be the case when post-process is disabled.
|
||||
// FIXME: we probably should take the dynamic scaling into account too
|
||||
// if MSAA is enabled, we end-up rendering in an intermediate buffer. This is the only case where
|
||||
// "!hasPostProcess" doesn't guarantee rendering into the swapchain.
|
||||
const bool useIntermediateBuffer = hasPostProcess || msaaOptions.enabled ||
|
||||
ssReflectionsOptions.enabled || hasScreenSpaceRefraction ||
|
||||
(isRenderingMultiview && engine.debug.stereo.
|
||||
combine_multiview_images);
|
||||
|
||||
// this is slightly ugly, but conceptually `pass` is const; it's just that we can't set
|
||||
// the scissor viewport during construction
|
||||
const_cast<RenderPass&>(pass).setScissorViewport(useIntermediateBuffer ? xvp : vp);
|
||||
|
||||
FrameGraphTexture::Descriptor colorBufferDesc = {
|
||||
.width = config.physicalViewport.width,
|
||||
.height = config.physicalViewport.height,
|
||||
@@ -1237,17 +1220,21 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
},
|
||||
colorBufferDesc, config, colorGradingConfigForColor, pass.getExecutor());
|
||||
|
||||
if (UTILS_UNLIKELY(hasScreenSpaceRefraction)) {
|
||||
if (view.isScreenSpaceRefractionEnabled() && !pass.empty()) {
|
||||
// This cancels the colorPass() call above if refraction is active.
|
||||
// The color pass + refraction + color-grading as subpass if needed
|
||||
colorPassOutput = RendererUtils::refractionPass(fg, mEngine, view, {
|
||||
auto const output = RendererUtils::refractionPass(fg, mEngine, view, {
|
||||
.shadows = blackboard.get<FrameGraphTexture>("shadows"),
|
||||
.ssao = blackboard.get<FrameGraphTexture>("ssao"),
|
||||
.ssr = ssrConfig.ssr,
|
||||
.structure = structure
|
||||
},
|
||||
config, ssrConfig, colorGradingConfigForColor,
|
||||
pass, firstRefractionCommand);
|
||||
config, ssrConfig, colorGradingConfigForColor, pass);
|
||||
|
||||
hasScreenSpaceRefraction = output.has_value();
|
||||
if (hasScreenSpaceRefraction) {
|
||||
colorPassOutput = output.value();
|
||||
}
|
||||
}
|
||||
|
||||
if (colorGradingConfig.customResolve) {
|
||||
|
||||
@@ -14,12 +14,15 @@ def _compare_goldens(base_dir, comparison_dir, out_dir=None):
|
||||
for f in all_files)
|
||||
all_results = []
|
||||
for test_dir in test_dirs:
|
||||
results_meta = {}
|
||||
results = []
|
||||
output_test_dir = None if not out_dir else os.path.abspath(os.path.join(out_dir, test_dir))
|
||||
output_test_dir = None if not out_dir else os.path.join(out_dir, test_dir)
|
||||
if output_test_dir:
|
||||
mkdir_p(output_test_dir)
|
||||
base_test_dir = os.path.abspath(os.path.join(base_dir, test_dir))
|
||||
comp_test_dir = os.path.abspath(os.path.join(comparison_dir, test_dir))
|
||||
results_meta['base_dir'] = base_test_dir
|
||||
results_meta['comparison_dir'] = comp_test_dir
|
||||
for golden_file in \
|
||||
glob.glob(os.path.join(base_test_dir, "*.tif")):
|
||||
base_fname = os.path.abspath(golden_file)
|
||||
@@ -42,12 +45,8 @@ def _compare_goldens(base_dir, comparison_dir, out_dir=None):
|
||||
result['result'] = RESULT_OK
|
||||
results.append(result)
|
||||
if output_test_dir:
|
||||
results_meta['results'] = results
|
||||
output_fname = os.path.join(output_test_dir, "compare_results.json")
|
||||
results_meta = {
|
||||
'results': results,
|
||||
'base_dir': os.path.relpath(output_fname, base_test_dir),
|
||||
'comparison_dir': os.path.relpath(output_fname, comp_test_dir),
|
||||
}
|
||||
with open(output_fname, 'w') as f:
|
||||
f.write(json.dumps(results_meta, indent=2))
|
||||
important_print(f'Written comparison results for {test_dir} to \n {output_fname}')
|
||||
@@ -65,7 +64,7 @@ if __name__ == '__main__':
|
||||
dest = args.dest
|
||||
if not dest:
|
||||
print('Assume the default renderdiff output folder')
|
||||
dest = os.path.join(os.getcwd(), './out/renderdiff')
|
||||
dest = os.path.join(os.getcwd(), './out/renderdiff_tests')
|
||||
assert os.path.exists(dest), f"Destination folder={dest} does not exist."
|
||||
|
||||
results = _compare_goldens(args.src, dest, out_dir=args.out)
|
||||
|
||||
@@ -17,9 +17,6 @@ import sys
|
||||
import flask
|
||||
import pathlib
|
||||
import json
|
||||
import requests
|
||||
import io
|
||||
import zipfile
|
||||
|
||||
from utils import ArgParseImpl
|
||||
|
||||
@@ -28,163 +25,14 @@ from flask import Flask, request, make_response, send_from_directory
|
||||
DIR = pathlib.Path(__file__).parent.absolute()
|
||||
HTML_DIR = os.path.join(DIR, "viewer_html")
|
||||
|
||||
# Generated by gemini
|
||||
def _download_github_artifacts(pr_number, github_token, output_dir= ".") -> None:
|
||||
"""
|
||||
Downloads artifacts associated with a specific GitHub Pull Request.
|
||||
|
||||
This function performs the following steps:
|
||||
1. Fetches the details of the Pull Request to get its head commit SHA.
|
||||
2. Searches for GitHub Actions workflow runs triggered by that specific commit.
|
||||
3. Iterates through successful workflow runs to find and list all associated artifacts.
|
||||
4. Downloads each artifact (which comes as a ZIP file).
|
||||
5. Extracts the contents of each downloaded ZIP file into a unique subdirectory
|
||||
within the specified output directory.
|
||||
|
||||
Args:
|
||||
owner (str): The GitHub repository owner (e.g., "octocat").
|
||||
repo (str): The GitHub repository name (e.g., "Spoon-Knife").
|
||||
pr_number (int): The Pull Request number.
|
||||
output_dir (str): The local directory where downloaded artifacts will be saved.
|
||||
Defaults to the current directory.
|
||||
"""
|
||||
|
||||
# Prepare HTTP headers for GitHub API requests
|
||||
headers = {"Accept": "application/vnd.github.v3+json"}
|
||||
if github_token:
|
||||
headers["Authorization"] = f"token {github_token}"
|
||||
|
||||
OWNER_REPO = 'google/filament'
|
||||
|
||||
# --- Step 1: Get PR details to find the head commit SHA ---
|
||||
print(f"Fetching details for PR #{pr_number} in {OWNER_REPO}...")
|
||||
pr_url = f"https://api.github.com/repos/{OWNER_REPO}/pulls/{pr_number}"
|
||||
try:
|
||||
response = requests.get(pr_url, headers=headers)
|
||||
response.raise_for_status() # Raise an exception for HTTP errors (4xx or 5xx)
|
||||
pr_data = response.json()
|
||||
commit_sha = pr_data["head"]["sha"]
|
||||
print(f"PR #{pr_number} is associated with commit SHA: {commit_sha}")
|
||||
except requests.exceptions.HTTPError as e:
|
||||
if e.response.status_code == 404:
|
||||
print(f"Error: PR #{pr_number} not found in {OWNER_REPO}. Please check the PR number, owner, and repository name.")
|
||||
elif e.response.status_code == 403:
|
||||
print(f"Error: Access forbidden to PR #{pr_number}. You might be hitting API rate limits or need a valid GitHub Token.")
|
||||
else:
|
||||
print(f"An HTTP error occurred while fetching PR details: {e}")
|
||||
return # Exit function on error
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"A network error occurred while fetching PR details: {e}")
|
||||
return # Exit function on error
|
||||
|
||||
# --- Step 2: Find workflow runs associated with the commit SHA ---
|
||||
print(f"Searching for workflow runs for commit SHA: {commit_sha}...")
|
||||
workflow_runs_url = f"https://api.github.com/repos/{OWNER_REPO}/actions/runs"
|
||||
# Filter by head_sha and event='pull_request' for precision
|
||||
params = {"head_sha": commit_sha, "event": "pull_request"}
|
||||
try:
|
||||
response = requests.get(workflow_runs_url, headers=headers, params=params)
|
||||
response.raise_for_status()
|
||||
runs_data = response.json()
|
||||
workflow_runs = runs_data.get("workflow_runs", [])
|
||||
|
||||
if not workflow_runs:
|
||||
print(f"No workflow runs found directly associated with PR #{pr_number} (commit SHA: {commit_sha}).")
|
||||
print("This might happen if the workflow was triggered by a push after the PR was opened,")
|
||||
print("or if the PR head branch was updated without triggering a new workflow run with this exact SHA.")
|
||||
print("Consider checking GitHub Actions runs manually for this PR's branch on GitHub.")
|
||||
return None
|
||||
|
||||
# Filter for runs that completed successfully
|
||||
successful_runs = [run for run in workflow_runs if run.get("conclusion") == "success" and run.get("status") == "completed"]
|
||||
if not successful_runs:
|
||||
print(f"No *successful and completed* workflow runs found for PR #{pr_number} with commit SHA {commit_sha}. Exiting.")
|
||||
return None
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f"An HTTP error occurred while searching for workflow runs: {e}")
|
||||
return None
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"A network error occurred while searching for workflow runs: {e}")
|
||||
return None
|
||||
|
||||
# Create the main output directory if it doesn't exist
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
print(f"Ensuring output directory exists: {os.path.abspath(output_dir)}")
|
||||
|
||||
downloaded_any_artifact = False # Flag to track if any artifact was downloaded
|
||||
|
||||
# --- Step 3 & 4: List and Download Artifacts for each successful run ---
|
||||
for run in successful_runs:
|
||||
run_id = run["id"]
|
||||
run_name = run["name"]
|
||||
print(f"\nProcessing workflow run '{run_name}' (ID: {run_id})...")
|
||||
|
||||
artifacts_url = f"https://api.github.com/repos/{OWNER_REPO}/actions/runs/{run_id}/artifacts"
|
||||
try:
|
||||
response = requests.get(artifacts_url, headers=headers)
|
||||
response.raise_for_status()
|
||||
artifacts_data = response.json()
|
||||
artifacts = artifacts_data.get("artifacts", [])
|
||||
|
||||
if not artifacts:
|
||||
print(f" No artifacts found for workflow run ID {run_id}.")
|
||||
continue # Move to the next workflow run
|
||||
|
||||
for artifact in artifacts:
|
||||
artifact_id = artifact["id"]
|
||||
artifact_name = artifact["name"]
|
||||
archive_download_url = artifact["archive_download_url"]
|
||||
|
||||
print(f" Found artifact: '{artifact_name}' (ID: {artifact_id})")
|
||||
|
||||
# Perform the download request
|
||||
print(f" Downloading '{artifact_name}'...")
|
||||
# Use a copy of headers and specific Accept for ZIP download
|
||||
download_headers = headers.copy()
|
||||
download_headers["Accept"] = "application/vnd.github.v3+zip"
|
||||
download_response = requests.get(archive_download_url, headers=download_headers, stream=True)
|
||||
download_response.raise_for_status() # Check for errors in download
|
||||
|
||||
# --- Step 5: Extract the contents ---
|
||||
# Use BytesIO to handle the zip file content in memory without saving to a temporary file
|
||||
with io.BytesIO(download_response.content) as zip_buffer:
|
||||
try:
|
||||
with zipfile.ZipFile(zip_buffer, 'r') as zip_ref:
|
||||
# Create a unique subdirectory for each artifact to avoid file name conflicts
|
||||
extract_path = os.path.join(output_dir, f"{artifact_name}_{artifact_id}")
|
||||
os.makedirs(extract_path, exist_ok=True)
|
||||
zip_ref.extractall(extract_path)
|
||||
print(f" Successfully extracted '{artifact_name}' to '{extract_path}/'")
|
||||
downloaded_any_artifact = True
|
||||
except zipfile.BadZipFile:
|
||||
print(f" Error: Downloaded file for '{artifact_name}' is not a valid zip file. Skipping extraction.")
|
||||
except Exception as e:
|
||||
print(f" An error occurred during extraction of '{artifact_name}': {e}")
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f" An HTTP error occurred while fetching artifacts for run {run_id}: {e}")
|
||||
if e.response.status_code == 403:
|
||||
print(" This often means you need a GitHub Personal Access Token with 'repo' scope (even for public repos for artifact downloads).")
|
||||
continue # Continue processing the next workflow run
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f" A network error occurred while fetching artifacts for run {run_id}: {e}")
|
||||
continue # Continue processing the next workflow run
|
||||
|
||||
if not downloaded_any_artifact:
|
||||
print("\nNo artifacts were downloaded for the specified PR.")
|
||||
else:
|
||||
print("\nAll available artifacts have been processed.")
|
||||
return 'Done'
|
||||
|
||||
def _create_app(config):
|
||||
app = Flask(__name__)
|
||||
|
||||
client_config = config.copy()
|
||||
base_dir = client_config['base_dir']
|
||||
comparison_dir = client_config['comparison_dir']
|
||||
diff_dir = client_config['diff_dir']
|
||||
|
||||
base_dir = os.path.join(diff_dir, client_config['base_dir'])
|
||||
comparison_dir = os.path.join(diff_dir, client_config['comparison_dir'])
|
||||
|
||||
del client_config['base_dir']
|
||||
del client_config['comparison_dir']
|
||||
del client_config['diff_dir']
|
||||
@@ -219,37 +67,12 @@ def _create_app(config):
|
||||
if __name__ == '__main__':
|
||||
PORT = 8901
|
||||
parser = ArgParseImpl()
|
||||
parser.add_argument('--diff', type=str, help='Diff result directory')
|
||||
parser.add_argument('--pr_number', type=str, help='Pull request artifacts to examine')
|
||||
parser.add_argument('--github_token', type=str, help='Necessary for pull PR artifacts')
|
||||
parser.add_argument('--diff', help='Diff directory', required=True)
|
||||
args, _ = parser.parse_known_args(sys.argv[1:])
|
||||
|
||||
if not args.diff and not args.pr_number:
|
||||
print('Need to specify either a diff result directory or a Pull Request number')
|
||||
exit(1)
|
||||
|
||||
if args.diff and args.pr_number:
|
||||
print('Cannot specify both a diff result directory and a Pull Request number')
|
||||
exit(1)
|
||||
|
||||
fdir = args.diff
|
||||
if args.pr_number:
|
||||
if not args.github_token:
|
||||
print('Must provide --github_token to be able to download artifacts')
|
||||
exit(1)
|
||||
output_dir = f'/tmp/filament-pr{args.pr_number}-rdiff-result'
|
||||
res = _download_github_artifacts(args.pr_number, args.github_token, output_dir)
|
||||
if not res:
|
||||
print('Failed to retrieve PR artifacts')
|
||||
exit(1)
|
||||
|
||||
# TODO: Clean up the following so that we're not so specific on the paths diffs/presubmit
|
||||
directory_name = list(os.listdir(output_dir))[0]
|
||||
fdir = os.path.join(os.path.join(output_dir, directory_name), 'diffs/presubmit')
|
||||
|
||||
with open(os.path.join(fdir, 'compare_results.json'), 'r') as f:
|
||||
with open(os.path.join(args.diff, 'compare_results.json'), 'r') as f:
|
||||
config = json.loads(f.read())
|
||||
config['diff_dir'] = os.path.abspath(fdir)
|
||||
config['diff_dir'] = os.path.abspath(args.diff)
|
||||
|
||||
app = _create_app(config)
|
||||
from waitress import serve
|
||||
|
||||
Reference in New Issue
Block a user