Initial graphic pipeline targeting. The goal of this PR is to have an initial graphics pipeline with missing features. It should help identifying areas that requires engineering. Current state is that developers of the GPU module can help with the many smaller pieces that needs to be engineered in order to get it working. It is not intended for users or developers from other modules, but your welcome to learn and give feedback on the code and engineering part. We do expect that large parts of the code still needs to be re-engineered into a more future-proof implementation. **Some highlights**: - In Vulkan the state is kept in the pipeline. Therefore the state is tracked per pipeline. In the near future this could be used as a cache. More research is needed against the default pipeline cache that vulkan already provides. - This PR is based on the work that Kazashi Yoshioka already did. And include work from him in the next areas - Vertex attributes - Vertex data conversions - Pipeline state - Immediate support working. - This PR modifies the VKCommandBuffer to keep track of the framebuffer and its binding state(render pass). Some Vulkan commands require no render pass to be active, other require a render pass. As the order of our commands on API level can not be separated this PR introduces a state engine to keep track of the current state and desired state. This is a temporary solution, the final solution will be proposed when we have a pixel on the screen. At that time I expect that we can design a command encoder that supports all the cases we need. **Notices**: - This branch works on NVIDIA GPUs and has been validated on a Linux system. AMD is known not to work (stalls) and Intel GPUs have not been tested at all. Windows might work but hasn't been validated yet. - The graphics pipeline is implemented with pixels in mind, not with performance. Currently when a draw call is scheduled it is flushed and waited until it is finished drawing, before other draw calls can be scheduled. We expected the performance to be worse that it actually is, but we expect huge performance gains in the future. - Any advanced drawing (that is used by the image editor, compositor or 3d viewport) isn't implemented and might crash when used. - Using multiple windows or resizing of window isn't supported and will stall the system. Pull Request: https://projects.blender.org/blender/blender/pulls/106224
186 lines
4.6 KiB
C++
186 lines
4.6 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2023 Blender Foundation. */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "BLI_utility_mixins.hh"
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "vk_common.hh"
|
|
|
|
namespace blender::gpu {
|
|
class VKContext;
|
|
class VKCommandBuffer;
|
|
|
|
/**
|
|
* In vulkan multiple commands can be in flight simultaneously.
|
|
*
|
|
* These commands can share the same resources like descriptor sets
|
|
* or push constants. When between commands these resources are updated
|
|
* a new version of these resources should be created.
|
|
*
|
|
* When a resource is updated it should check the submission id of the
|
|
* command buffer. If it is different, then the resource can be reused.
|
|
* If the submission id is the same a new version of the resource to now
|
|
* intervene with other commands that uses the resource.
|
|
*
|
|
* VKSubmissionID is the identifier to keep track if a new submission is
|
|
* being recorded.
|
|
*/
|
|
struct VKSubmissionID {
|
|
private:
|
|
int64_t id_ = -1;
|
|
|
|
public:
|
|
VKSubmissionID() = default;
|
|
|
|
private:
|
|
/**
|
|
* Reset the submission id.
|
|
*
|
|
* This should only be called during initialization of the command buffer.
|
|
* As it leads to undesired behavior after resources are already tracking
|
|
* the submission id.
|
|
*/
|
|
void reset()
|
|
{
|
|
id_ = 0;
|
|
}
|
|
|
|
/**
|
|
* Change the submission id.
|
|
*
|
|
* Is called when submitting a command buffer to the queue. In this case resource
|
|
* known that the next time it is used that it can free its sub resources used by
|
|
* the previous submission.
|
|
*/
|
|
void next()
|
|
{
|
|
id_++;
|
|
}
|
|
|
|
public:
|
|
const VKSubmissionID &operator=(const VKSubmissionID &other)
|
|
{
|
|
id_ = other.id_;
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const VKSubmissionID &other)
|
|
{
|
|
return id_ == other.id_;
|
|
}
|
|
|
|
bool operator!=(const VKSubmissionID &other)
|
|
{
|
|
return id_ != other.id_;
|
|
}
|
|
|
|
friend class VKCommandBuffer;
|
|
};
|
|
|
|
/**
|
|
* Submission tracker keeps track of the last known submission id of the
|
|
* command buffer.
|
|
*/
|
|
class VKSubmissionTracker {
|
|
VKSubmissionID last_known_id_;
|
|
|
|
public:
|
|
/**
|
|
* Check if the submission_id has changed since the last time it was called
|
|
* on this VKSubmissionTracker.
|
|
*/
|
|
bool is_changed(VKContext &context);
|
|
};
|
|
|
|
/**
|
|
* VKResourceTracker will keep track of resources.
|
|
*/
|
|
template<typename Resource> class VKResourceTracker : NonCopyable {
|
|
VKSubmissionTracker submission_tracker_;
|
|
Vector<std::unique_ptr<Resource>> tracked_resources_;
|
|
|
|
protected:
|
|
VKResourceTracker<Resource>() = default;
|
|
VKResourceTracker<Resource>(VKResourceTracker<Resource> &&other)
|
|
: submission_tracker_(other.submission_tracker_),
|
|
tracked_resources_(std::move(other.tracked_resources_))
|
|
{
|
|
}
|
|
|
|
VKResourceTracker<Resource> &operator=(VKResourceTracker<Resource> &&other)
|
|
{
|
|
submission_tracker_ = other.submission_tracker_;
|
|
tracked_resources_ = std::move(other.tracked_resources_);
|
|
return *this;
|
|
}
|
|
|
|
virtual ~VKResourceTracker()
|
|
{
|
|
free_tracked_resources();
|
|
}
|
|
|
|
/**
|
|
* Get a resource what can be used by the resource tracker.
|
|
*
|
|
* When a different submission was detected all previous resources
|
|
* will be freed and a new resource will be returned.
|
|
*
|
|
* When still in the same submission and we need to update the resource
|
|
* (is_dirty=true) then a new resource will be returned. Otherwise
|
|
* the previous used resource will be used.
|
|
*
|
|
* When no resources exists, a new resource will be created.
|
|
*
|
|
* The resource given back is owned by this resource tracker. And
|
|
* the resource should not be stored outside this class as it might
|
|
* be destroyed when the next submission is detected.
|
|
*/
|
|
std::unique_ptr<Resource> &tracked_resource_for(VKContext &context, const bool is_dirty)
|
|
{
|
|
if (submission_tracker_.is_changed(context)) {
|
|
free_tracked_resources();
|
|
tracked_resources_.append(create_resource(context));
|
|
}
|
|
else if (is_dirty || tracked_resources_.is_empty()) {
|
|
tracked_resources_.append(create_resource(context));
|
|
}
|
|
return active_resource();
|
|
}
|
|
|
|
/**
|
|
* Callback to create a new resource. Can be called by the `tracked_resource_for` method.
|
|
*/
|
|
virtual std::unique_ptr<Resource> create_resource(VKContext &context) = 0;
|
|
|
|
/**
|
|
* Does this instance have an active resource.
|
|
*/
|
|
bool has_active_resource()
|
|
{
|
|
return !tracked_resources_.is_empty();
|
|
}
|
|
|
|
/**
|
|
* Return the active resource of the tracker.
|
|
*/
|
|
std::unique_ptr<Resource> &active_resource()
|
|
{
|
|
BLI_assert(!tracked_resources_.is_empty());
|
|
return tracked_resources_.last();
|
|
}
|
|
|
|
private:
|
|
void free_tracked_resources()
|
|
{
|
|
tracked_resources_.clear();
|
|
}
|
|
};
|
|
|
|
} // namespace blender::gpu
|