Buffered rendering is starting to work, though still kinda broken.

This commit is contained in:
Henrik Rydgard 2017-05-21 23:13:53 +02:00 committed by Henrik Rydgård
parent 2b93338255
commit 0c70735bc4
12 changed files with 86 additions and 36 deletions

View File

@ -1307,6 +1307,9 @@ void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageAspectFlag
if (old_image_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
image_memory_barrier.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT;
}
if (old_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
image_memory_barrier.srcAccessMask |= VK_ACCESS_SHADER_READ_BIT;
}
if (old_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
image_memory_barrier.srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

View File

@ -362,6 +362,14 @@ void VulkanTexture::EndCreate() {
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
void VulkanTexture::TransitionForUpload() {
VkCommandBuffer cmd = vulkan_->GetInitCommandBuffer();
TransitionImageLayout(cmd, image,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
}
void VulkanTexture::Destroy() {
if (view != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteImageView(view);

View File

@ -34,6 +34,9 @@ public:
bool CreateDirect(int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr);
void UploadMip(int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
void EndCreate();
void TransitionForUpload();
int GetNumMips() const { return numMips_; }
void Destroy();

View File

@ -704,6 +704,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
if (useBufferedRendering_ && vfb && vfb->fbo) {
draw_->BindFramebufferAsRenderTarget(vfb->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP });
SetViewport2D(0, 0, vfb->renderWidth, vfb->renderHeight);
draw_->SetScissorRect(0, 0, vfb->renderWidth, vfb->renderHeight);
} else {
// We are drawing to the back buffer so need to flip.
if (needBackBufferYSwap_)
@ -711,6 +712,7 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
float x, y, w, h;
CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
SetViewport2D(x, y, w, h);
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
}
DisableState();
@ -894,6 +896,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
}
// Just a pointer to plain memory to draw. We should create a framebuffer, then draw to it.
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
DrawFramebufferToOutput(Memory::GetPointer(displayFramebufPtr_), displayFormat_, displayStride_, true);
return;
}
@ -942,6 +945,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
draw_->BindFramebufferAsTexture(vfb->fbo, 0, Draw::FB_COLOR_BIT, 0);
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
bool linearFilter = g_Config.iBufFilter == SCALE_LINEAR;
// We are doing the DrawActiveTexture call directly to the backbuffer here. Hence, we must
// flip V.
@ -967,6 +971,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
int fbo_w, fbo_h;
draw_->GetFramebufferDimensions(extraFBOs_[0], &fbo_w, &fbo_h);
SetViewport2D(0, 0, fbo_w, fbo_h);
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
shaderManager_->DirtyLastShader(); // dirty lastShader_
PostShaderUniforms uniforms{};
CalculatePostShaderUniforms(vfb->bufferWidth, vfb->bufferHeight, renderWidth_, renderHeight_, &uniforms);
@ -1013,6 +1018,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput() {
}*/
} else {
draw_->BindFramebufferAsRenderTarget(nullptr, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
// We are doing the DrawActiveTexture call directly to the backbuffer here. Hence, we must
// flip V.
if (needBackBufferYSwap_)

View File

@ -68,7 +68,6 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra
: vulkan_(vulkan),
draw_(draw),
prevPrim_(GE_PRIM_INVALID),
lastVTypeID_(-1),
numDrawCalls(0),
vertexCountInDrawCalls(0),
curFrame_(0),
@ -329,10 +328,12 @@ inline void DrawEngineVulkan::SetupVertexDecoderInternal(u32 vertType) {
const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24);
// If vtype has changed, setup the vertex decoder.
if (vertTypeID != lastVTypeID_) {
if (vertTypeID != lastVType_) {
dec_ = GetVertexDecoder(vertTypeID);
lastVTypeID_ = vertTypeID;
lastVType_ = vertTypeID;
}
if (!dec_)
Crash();
}
void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) {
@ -710,7 +711,7 @@ void DrawEngineVulkan::DoFlush() {
}
prim = indexGen.Prim();
bool hasColor = (lastVTypeID_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
} else {
@ -743,7 +744,7 @@ void DrawEngineVulkan::DoFlush() {
dirtyUniforms_ |= shaderManager_->UpdateUniforms();
shaderManager_->GetShaders(prim, lastVTypeID_, &vshader, &fshader, useHWTransform);
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, dec_, vshader, fshader, true);
if (!pipeline) {
// Already logged, let's bail out.
@ -778,7 +779,7 @@ void DrawEngineVulkan::DoFlush() {
} else {
// Decode to "decoded"
DecodeVerts(nullptr, nullptr, nullptr);
bool hasColor = (lastVTypeID_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
} else {
@ -849,7 +850,7 @@ void DrawEngineVulkan::DoFlush() {
dirtyUniforms_ |= shaderManager_->UpdateUniforms();
shaderManager_->GetShaders(prim, lastVTypeID_, &vshader, &fshader, useHWTransform);
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, useHWTransform);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey, dec_, vshader, fshader, false);
if (!pipeline) {
// Already logged, let's bail out.

View File

@ -197,8 +197,6 @@ private:
IndexGenerator indexGen;
GEPrimitiveType prevPrim_;
u32 lastVTypeID_;
TransformedVertex *transformed = nullptr;
TransformedVertex *transformedExpanded = nullptr;

View File

@ -129,9 +129,9 @@ void FramebufferManagerVulkan::InitDeviceObjects() {
assert(fsBasicTex_ != VK_NULL_HANDLE);
assert(vsBasicTex_ != VK_NULL_HANDLE);
// Get a representative render pass and use when creating the pipeline.
pipelineBasicTexBackBuffer_ = vulkan2D_.GetPipeline(pipelineCache2D_, vulkan_->GetSurfaceRenderPass(), vsBasicTex_, fsBasicTex_);
pipelineBasicTexFrameBuffer_ = vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS), vsBasicTex_, fsBasicTex_);
// Prime the 2D pipeline cache.
vulkan2D_.GetPipeline(pipelineCache2D_, vulkan_->GetSurfaceRenderPass(), vsBasicTex_, fsBasicTex_);
vulkan2D_.GetPipeline(pipelineCache2D_, (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS), vsBasicTex_, fsBasicTex_);
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
@ -240,6 +240,8 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
drawPixelsTex_->CreateDirect(width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
// Initialize backbuffer texture for DrawPixels
drawPixelsTexFormat_ = srcPixelFormat;
} else {
drawPixelsTex_->TransitionForUpload();
}
// TODO: We can just change the texture format and flip some bits around instead of this.
@ -314,11 +316,6 @@ void FramebufferManagerVulkan::SetViewport2D(int x, int y, int w, int h) {
}
void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, bool linearFilter) {
// TODO
}
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.
void FramebufferManagerVulkan::DrawTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation) {
float texCoords[8] = {
u0,v0,
u1,v0,
@ -354,13 +351,18 @@ void FramebufferManagerVulkan::DrawTexture(VulkanTexture *texture, float x, floa
vtx[i].y = vtx[i].y * invDestH - 1.0f;
}
draw_->FlushState();
// TODO: Should probably use draw_ directly and not go low level
VulkanPushBuffer *push = frameData_[curFrame_].push_;
VkCommandBuffer cmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::RENDERPASS_COMMANDBUFFER);
// TODO: Choose linear or nearest appropriately, see GL impl.
vulkan2D_.BindDescriptorSet(cmd, texture->GetImageView(), linearSampler_);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkImageView view = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE_IMAGEVIEW);
vulkan2D_.BindDescriptorSet(cmd, view, linearFilter ? linearSampler_ : nearestSampler_);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, cur2DPipeline_);
VkBuffer vbuffer;
VkDeviceSize offset = push->Push(vtx, sizeof(vtx), &vbuffer);
vkCmdBindVertexBuffers(cmd, 0, 1, &vbuffer, &offset);
@ -368,7 +370,8 @@ void FramebufferManagerVulkan::DrawTexture(VulkanTexture *texture, float x, floa
}
void FramebufferManagerVulkan::Bind2DShader() {
VkRenderPass rp = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::COMPATIBLE_RENDERPASS);
cur2DPipeline_ = vulkan2D_.GetPipeline(pipelineCache2D_, rp, vsBasicTex_, fsBasicTex_);
}
void FramebufferManagerVulkan::BindPostShader(const PostShaderUniforms &uniforms) {

View File

@ -69,9 +69,7 @@ public:
drawEngine_ = td;
}
// If texture != 0, will bind it.
// x,y,w,h are relative to destW, destH which fill out the target completely.
void DrawTexture(VulkanTexture *texture, float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, VkPipeline pipeline, int uvRotation);
void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, bool linearFilter) override;
void DestroyAllFBOs();
@ -188,6 +186,8 @@ private:
VkPipeline pipelineBasicTexBackBuffer_;
VkPipeline pipelineBasicTexFrameBuffer_;
VkPipeline cur2DPipeline_;
// Postprocessing
VkPipeline pipelinePostShader_;

View File

@ -217,6 +217,7 @@ VkPipeline Vulkan2D::GetPipeline(VkPipelineCache cache, VkRenderPass rp, VkShade
PipelineKey key;
key.vs = vs;
key.fs = fs;
key.rp = rp;
auto iter = pipelines_.find(key);
if (iter != pipelines_.end()) {

View File

@ -132,6 +132,10 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep
return false;
if (msgCode == 11)
return false;
// Silence "invalid reads of buffer data" - usually just uninitialized color buffers that will immediately get cleared due to our
// lacking clearing optimizations.
if (msgCode == 15 && objType == VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT)
return false;
#ifdef _WIN32
std::string msg = message.str();
@ -144,7 +148,7 @@ static VkBool32 VKAPI_CALL Vulkan_Dbg(VkDebugReportFlagsEXT msgFlags, VkDebugRep
MessageBoxA(NULL, message.str().c_str(), "Alert", MB_OK);
}
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
if (options->breakOnWarning) {
if (options->breakOnWarning && IsDebuggerPresent()) {
DebugBreak();
}
}

View File

@ -677,6 +677,16 @@ public:
virtual void HandleEvent(Event ev, int width, int height, void *param1 = nullptr, void *param2 = nullptr) = 0;
// This flushes command buffers and waits for execution at the point of the end of the last
// renderpass that wrote to the requested framebuffer. This is needed before trying to read it back
// on modern APIs like Vulkan. Ifr the framebuffer is currently being rendered to, we'll just end the render pass.
// The next draw call will automatically start up a new one.
// APIs like OpenGL won't need to implement this one.
virtual void WaitRenderCompletion(Framebuffer *fbo) {}
// Flush state like scissors etc so the caller can do its own custom drawing.
virtual void FlushState() {}
protected:
void CreatePresets();

View File

@ -422,6 +422,11 @@ public:
void BeginFrame() override;
void EndFrame() override;
void FlushState() override {
ApplyDynamicState();
}
void WaitRenderCompletion(Framebuffer *fbo) override;
std::string GetInfoString(InfoField info) const override {
// TODO: Make these actually query the right information
switch (info) {
@ -447,15 +452,14 @@ public:
switch (obj) {
case NativeObject::COMPATIBLE_RENDERPASS:
// Return a representative renderpass.
return (uintptr_t)(curRenderPass_ == vulkan_->GetSurfaceRenderPass() ? curRenderPass_ : renderPasses_[0]);
if (curRenderPass_ == vulkan_->GetSurfaceRenderPass())
return (uintptr_t)curRenderPass_;
else
return (uintptr_t)renderPasses_[0];
case NativeObject::RENDERPASS_COMMANDBUFFER:
return (uintptr_t)cmd_;
case NativeObject::BOUND_TEXTURE_IMAGEVIEW:
if (boundTextures_[0]) {
return (uintptr_t)boundTextures_[0]->GetImageView();
} else {
return 0;
}
return (uintptr_t)boundImageView_[0];
default:
return 0;
}
@ -508,11 +512,13 @@ private:
};
VKTexture *boundTextures_[MAX_BOUND_TEXTURES];
VKSamplerState *boundSamplers_[MAX_BOUND_TEXTURES];
VkImageView boundImageView_[MAX_BOUND_TEXTURES];
struct FrameData {
VulkanPushBuffer *pushBuffer;
VkCommandPool cmdPool_;
VkCommandBuffer cmdBufs[MAX_FRAME_COMMAND_BUFFERS];
int startCmdBufs_;
int numCmdBufs_;
// Per-frame descriptor set cache. As it's per frame and reset every frame, we don't need to
@ -863,17 +869,18 @@ VkCommandBuffer VKContext::AllocCmdBuf() {
void VKContext::BeginFrame() {
vulkan_->BeginFrame();
FrameData *frame = &frame_[frameNum_ & 1];
frame->numCmdBufs_ = 0;
vkResetCommandPool(vulkan_->GetDevice(), frame->cmdPool_, 0);
push_ = frame->pushBuffer;
FrameData &frame = frame_[frameNum_ & 1];
frame.startCmdBufs_ = 0;
frame.numCmdBufs_ = 0;
vkResetCommandPool(vulkan_->GetDevice(), frame.cmdPool_, 0);
push_ = frame.pushBuffer;
// OK, we now know that nothing is reading from this frame's data pushbuffer,
push_->Reset();
push_->Begin(vulkan_);
frame->descSets_.clear();
VkResult result = vkResetDescriptorPool(device_, frame->descriptorPool, 0);
frame.descSets_.clear();
VkResult result = vkResetDescriptorPool(device_, frame.descriptorPool, 0);
assert(result == VK_SUCCESS);
scissor_.extent.width = pixel_xres;
@ -882,6 +889,10 @@ void VKContext::BeginFrame() {
viewportDirty_ = true;
}
void VKContext::WaitRenderCompletion(Framebuffer *fbo) {
// TODO
}
void VKContext::EndFrame() {
if (curRenderPass_) {
vulkan_->EndSurfaceRenderPass();
@ -891,7 +902,7 @@ void VKContext::EndFrame() {
// Cap off and submit all the command buffers we recorded during the frame.
FrameData &frame = frame_[frameNum_ & 1];
for (int i = 0; i < frame.numCmdBufs_; i++) {
for (int i = frame.startCmdBufs_; i < frame.numCmdBufs_; i++) {
vkEndCommandBuffer(frame.cmdBufs[i]);
vulkan_->QueueBeforeSurfaceRender(frame.cmdBufs[i]);
}
@ -1204,6 +1215,7 @@ void VKContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset,
void VKContext::BindTextures(int start, int count, Texture **textures) {
for (int i = start; i < start + count; i++) {
boundTextures_[i] = static_cast<VKTexture *>(textures[i]);
boundImageView_[i] = boundTextures_[i]->GetImageView();
}
}
@ -1719,6 +1731,7 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne
// we're between passes so it's OK.
vkCmdPipelineBarrier(transitionCmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, 0, 0, 0, 1, &barrier);
fb->color.layout = barrier.newLayout;
boundImageView_[0] = fb->color.view;
}
void VKContext::BindFramebufferForRead(Framebuffer *fbo) { /* noop */ }