Commit 17448956 by Frank Henigman Committed by Commit Bot

Vulkan: vertex attributes in client memory.

Support vertex data stored in client memory passed to glVertexAttribPointer. Only GL_FLOAT data is supported at this time. Includes a simple test. BUG=angleproject:1683 Change-Id: I3bc0cdefe02b02c046b0e85822019a0f1762235e Reviewed-on: https://chromium-review.googlesource.com/425137 Commit-Queue: Frank Henigman <fjhenigman@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent b66de58f
......@@ -67,7 +67,8 @@ ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer)
mRenderer(renderer),
mCurrentDrawMode(GL_NONE),
mVertexArrayDirty(false),
mTexturesDirty(false)
mTexturesDirty(false),
mStreamingVertexData(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 1024 * 1024)
{
memset(&mClearColorValue, 0, sizeof(mClearColorValue));
memset(&mClearDepthStencilValue, 0, sizeof(mClearDepthStencilValue));
......@@ -82,6 +83,7 @@ void ContextVk::onDestroy(const gl::Context *context)
VkDevice device = mRenderer->getDevice();
mDescriptorPool.destroy(device);
mStreamingVertexData.destroy(device);
}
gl::Error ContextVk::initialize()
......@@ -156,6 +158,8 @@ gl::Error ContextVk::initPipeline(const gl::Context *context)
gl::Error ContextVk::setupDraw(const gl::Context *context,
GLenum mode,
DrawType drawType,
int firstVertex,
int lastVertex,
vk::CommandBuffer **commandBuffer)
{
if (mode != mCurrentDrawMode)
......@@ -169,22 +173,16 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
ANGLE_TRY(initPipeline(context));
}
const auto &state = mState.getState();
const auto &state = mState.getState();
const gl::Program *programGL = state.getProgram();
ProgramVk *programVk = vk::GetImpl(programGL);
ProgramVk *programVk = vk::GetImpl(programGL);
const gl::VertexArray *vao = state.getVertexArray();
VertexArrayVk *vkVAO = vk::GetImpl(vao);
const auto *drawFBO = state.getDrawFramebuffer();
FramebufferVk *vkFBO = vk::GetImpl(drawFBO);
VertexArrayVk *vkVAO = vk::GetImpl(vao);
const auto *drawFBO = state.getDrawFramebuffer();
FramebufferVk *vkFBO = vk::GetImpl(drawFBO);
Serial queueSerial = mRenderer->getCurrentQueueSerial();
uint32_t maxAttrib = programGL->getState().getMaxActiveAttribLocation();
// Process vertex attributes. Assume zero offsets for now.
// TODO(jmadill): Offset handling.
const auto &vertexHandles = vkVAO->getCurrentArrayBufferHandles();
angle::MemoryBuffer *zeroBuf = nullptr;
ANGLE_TRY(context->getZeroFilledBuffer(maxAttrib * sizeof(VkDeviceSize), &zeroBuf));
// TODO(jmadill): Need to link up the TextureVk to the Secondary CB.
vk::CommandBufferNode *renderNode = nullptr;
ANGLE_TRY(vkFBO->getRenderNode(context, &renderNode));
......@@ -233,9 +231,11 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
}
(*commandBuffer)->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline->get());
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(vkVAO->streamVertexData(contextVk, &mStreamingVertexData, firstVertex, lastVertex));
(*commandBuffer)
->bindVertexBuffers(0, maxAttrib, vertexHandles.data(),
reinterpret_cast<const VkDeviceSize *>(zeroBuf->data()));
->bindVertexBuffers(0, maxAttrib, vkVAO->getCurrentArrayBufferHandles().data(),
vkVAO->getCurrentArrayBufferOffsets().data());
// Update the queue serial for the pipeline object.
ASSERT(mCurrentPipeline && mCurrentPipeline->valid());
......@@ -264,7 +264,7 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
gl::Error ContextVk::drawArrays(const gl::Context *context, GLenum mode, GLint first, GLsizei count)
{
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(setupDraw(context, mode, DrawType::Arrays, &commandBuffer));
ANGLE_TRY(setupDraw(context, mode, DrawType::Arrays, first, first + count - 1, &commandBuffer));
commandBuffer->draw(count, 1, first, 0);
return gl::NoError();
}
......@@ -286,7 +286,8 @@ gl::Error ContextVk::drawElements(const gl::Context *context,
const void *indices)
{
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(setupDraw(context, mode, DrawType::Elements, &commandBuffer));
// TODO(fjhenigman): calculate the index range and pass to setupDraw()
ANGLE_TRY(setupDraw(context, mode, DrawType::Elements, 0, 0, &commandBuffer));
if (indices)
{
......
......@@ -13,6 +13,7 @@
#include <vulkan/vulkan.h>
#include "libANGLE/renderer/ContextImpl.h"
#include "libANGLE/renderer/vulkan/StreamingBuffer.h"
#include "libANGLE/renderer/vulkan/vk_cache_utils.h"
namespace rx
......@@ -165,6 +166,8 @@ class ContextVk : public ContextImpl
gl::Error setupDraw(const gl::Context *context,
GLenum mode,
DrawType drawType,
int firstVertex,
int lastVertex,
vk::CommandBuffer **commandBuffer);
RendererVk *mRenderer;
......@@ -176,7 +179,7 @@ class ContextVk : public ContextImpl
std::unique_ptr<vk::PipelineDesc> mPipelineDesc;
// The descriptor pool is externally sychronized, so cannot be accessed from different threads
// simulataneously. Hence, we keep it in the ContextVk instead of the RendererVk.
// simultaneously. Hence, we keep it in the ContextVk instead of the RendererVk.
vk::DescriptorPool mDescriptorPool;
// Triggers adding dependencies to the command graph.
......@@ -186,6 +189,8 @@ class ContextVk : public ContextImpl
// Cached clear value for color and depth/stencil.
VkClearValue mClearColorValue;
VkClearValue mClearDepthStencilValue;
StreamingBuffer mStreamingVertexData;
};
} // namespace rx
......
//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// StreamingBuffer:
// Create, map and flush buffers as needed to hold data, returning a handle and offset for each
// chunk.
//
#include "StreamingBuffer.h"
#include "anglebase/numerics/safe_math.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
namespace rx
{
StreamingBuffer::StreamingBuffer(VkBufferUsageFlags usage, size_t minSize)
: mUsage(usage),
mMinSize(minSize),
mNextWriteOffset(0),
mLastFlushOffset(0),
mSize(0),
mMappedMemory(nullptr)
{
}
StreamingBuffer::~StreamingBuffer()
{
}
gl::Error StreamingBuffer::allocate(ContextVk *context,
size_t allocationSize,
uint8_t **ptrOut,
VkBuffer *handleOut,
VkDeviceSize *offsetOut)
{
RendererVk *renderer = context->getRenderer();
// TODO(fjhenigman): Update this when we have buffers that need to
// persist longer than one frame.
updateQueueSerial(renderer->getCurrentQueueSerial());
angle::base::CheckedNumeric<size_t> checkedNextWriteOffset = mNextWriteOffset;
checkedNextWriteOffset += allocationSize;
if (!checkedNextWriteOffset.IsValid() || checkedNextWriteOffset.ValueOrDie() > mSize)
{
VkDevice device = context->getDevice();
if (mMappedMemory)
{
mMemory.unmap(device);
mMappedMemory = nullptr;
}
renderer->releaseResource(*this, &mBuffer);
renderer->releaseResource(*this, &mMemory);
VkBufferCreateInfo createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.size = std::max(allocationSize, mMinSize);
createInfo.usage = mUsage;
createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0;
createInfo.pQueueFamilyIndices = nullptr;
ANGLE_TRY(mBuffer.init(device, createInfo));
ANGLE_TRY(vk::AllocateBufferMemory(renderer, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mBuffer,
&mMemory, &mSize));
ANGLE_TRY(mMemory.map(device, 0, mSize, 0, &mMappedMemory));
mNextWriteOffset = 0;
mLastFlushOffset = 0;
}
ASSERT(mBuffer.valid());
*handleOut = mBuffer.getHandle();
ASSERT(mMappedMemory);
*ptrOut = mMappedMemory + mNextWriteOffset;
*offsetOut = mNextWriteOffset;
mNextWriteOffset += allocationSize;
return gl::NoError();
}
gl::Error StreamingBuffer::flush(ContextVk *context)
{
if (mNextWriteOffset > mLastFlushOffset)
{
VkMappedMemoryRange range;
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
range.pNext = nullptr;
range.memory = mMemory.getHandle();
range.offset = mLastFlushOffset;
range.size = mNextWriteOffset - mLastFlushOffset;
ANGLE_VK_TRY(vkFlushMappedMemoryRanges(context->getDevice(), 1, &range));
mLastFlushOffset = mNextWriteOffset;
}
return gl::NoError();
}
void StreamingBuffer::destroy(VkDevice device)
{
mBuffer.destroy(device);
mMemory.destroy(device);
}
} // namespace rx
//
// Copyright 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// StreamingBuffer:
// Create, map and flush buffers as needed to hold data, returning a handle and offset for each
// chunk.
//
#ifndef LIBANGLE_RENDERER_VULKAN_STREAMING_BUFFER_H_
#define LIBANGLE_RENDERER_VULKAN_STREAMING_BUFFER_H_
#include "libANGLE/renderer/vulkan/vk_utils.h"
namespace rx
{
class StreamingBuffer : public ResourceVk
{
public:
StreamingBuffer(VkBufferUsageFlags usage, size_t minSize);
~StreamingBuffer();
gl::Error allocate(ContextVk *context,
size_t amount,
uint8_t **ptrOut,
VkBuffer *handleOut,
VkDeviceSize *offsetOut);
gl::Error flush(ContextVk *context);
void destroy(VkDevice device);
private:
VkBufferUsageFlags mUsage;
size_t mMinSize;
vk::Buffer mBuffer;
vk::DeviceMemory mMemory;
size_t mNextWriteOffset;
size_t mLastFlushOffset;
size_t mSize;
uint8_t *mMappedMemory;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_STREAMING_BUFFER_H_
......@@ -23,10 +23,12 @@ namespace rx
VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state)
: VertexArrayImpl(state),
mCurrentArrayBufferHandles{},
mCurrentArrayBufferOffsets{},
mCurrentArrayBufferResources{},
mCurrentElementArrayBufferResource(nullptr)
{
mCurrentArrayBufferHandles.fill(VK_NULL_HANDLE);
mCurrentArrayBufferOffsets.fill(0);
mCurrentArrayBufferResources.fill(nullptr);
mPackedInputBindings.fill({0, 0});
......@@ -41,6 +43,53 @@ void VertexArrayVk::destroy(const gl::Context *context)
{
}
gl::Error VertexArrayVk::streamVertexData(ContextVk *context,
StreamingBuffer *stream,
int firstVertex,
int lastVertex)
{
const auto &attribs = mState.getVertexAttributes();
const auto &bindings = mState.getVertexBindings();
const gl::Program *programGL = context->getGLState().getProgram();
// TODO(fjhenigman): When we have a bunch of interleaved attributes, they end up
// un-interleaved, wasting space and copying time. Consider improving on that.
for (auto attribIndex : programGL->getActiveAttribLocationsMask())
{
const auto &attrib = attribs[attribIndex];
const auto &binding = bindings[attrib.bindingIndex];
gl::Buffer *bufferGL = binding.getBuffer().get();
if (attrib.enabled && !bufferGL)
{
// TODO(fjhenigman): Work with more formats than just GL_FLOAT.
if (attrib.type != GL_FLOAT)
{
UNIMPLEMENTED();
return gl::InternalError();
}
// Only [firstVertex, lastVertex] is needed by the upcoming draw so that
// is all we copy, but we allocate space for [0, lastVertex] so indexing
// will work. If we don't start at zero all the indices will be off.
// TODO(fjhenigman): See if we can account for indices being off by adjusting
// the offset, thus avoiding wasted memory.
const size_t firstByte = firstVertex * binding.getStride();
const size_t lastByte =
lastVertex * binding.getStride() + gl::ComputeVertexAttributeTypeSize(attrib);
uint8_t *dst = nullptr;
ANGLE_TRY(stream->allocate(context, lastByte, &dst,
&mCurrentArrayBufferHandles[attribIndex],
&mCurrentArrayBufferOffsets[attribIndex]));
memcpy(dst + firstByte, static_cast<const uint8_t *>(attrib.pointer) + firstByte,
lastByte - firstByte);
}
}
ANGLE_TRY(stream->flush(context));
return gl::NoError();
}
void VertexArrayVk::syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits)
{
......@@ -94,6 +143,8 @@ void VertexArrayVk::syncState(const gl::Context *context,
mCurrentArrayBufferResources[attribIndex] = nullptr;
mCurrentArrayBufferHandles[attribIndex] = VK_NULL_HANDLE;
}
// TODO(jmadill): Offset handling. Assume zero for now.
mCurrentArrayBufferOffsets[attribIndex] = 0;
}
else
{
......@@ -107,6 +158,11 @@ const gl::AttribArray<VkBuffer> &VertexArrayVk::getCurrentArrayBufferHandles() c
return mCurrentArrayBufferHandles;
}
const gl::AttribArray<VkDeviceSize> &VertexArrayVk::getCurrentArrayBufferOffsets() const
{
return mCurrentArrayBufferOffsets;
}
void VertexArrayVk::updateDrawDependencies(vk::CommandBufferNode *readNode,
const gl::AttributesMask &activeAttribsMask,
Serial serial,
......@@ -115,8 +171,8 @@ void VertexArrayVk::updateDrawDependencies(vk::CommandBufferNode *readNode,
// Handle the bound array buffers.
for (auto attribIndex : activeAttribsMask)
{
ASSERT(mCurrentArrayBufferResources[attribIndex]);
mCurrentArrayBufferResources[attribIndex]->onReadResource(readNode, serial);
if (mCurrentArrayBufferResources[attribIndex])
mCurrentArrayBufferResources[attribIndex]->onReadResource(readNode, serial);
}
// Handle the bound element array buffer.
......
......@@ -16,6 +16,7 @@
namespace rx
{
class BufferVk;
class StreamingBuffer;
class VertexArrayVk : public VertexArrayImpl
{
......@@ -25,10 +26,15 @@ class VertexArrayVk : public VertexArrayImpl
void destroy(const gl::Context *context) override;
gl::Error streamVertexData(ContextVk *context,
StreamingBuffer *stream,
int firstVertex,
int lastVertex);
void syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits) override;
const gl::AttribArray<VkBuffer> &getCurrentArrayBufferHandles() const;
const gl::AttribArray<VkDeviceSize> &getCurrentArrayBufferOffsets() const;
void updateDrawDependencies(vk::CommandBufferNode *readNode,
const gl::AttributesMask &activeAttribsMask,
......@@ -49,6 +55,7 @@ class VertexArrayVk : public VertexArrayImpl
const gl::VertexAttribute &attrib);
gl::AttribArray<VkBuffer> mCurrentArrayBufferHandles;
gl::AttribArray<VkDeviceSize> mCurrentArrayBufferOffsets;
gl::AttribArray<ResourceVk *> mCurrentArrayBufferResources;
ResourceVk *mCurrentElementArrayBufferResource;
......
......@@ -748,6 +748,8 @@
'libANGLE/renderer/vulkan/SamplerVk.h',
'libANGLE/renderer/vulkan/ShaderVk.cpp',
'libANGLE/renderer/vulkan/ShaderVk.h',
'libANGLE/renderer/vulkan/StreamingBuffer.h',
'libANGLE/renderer/vulkan/StreamingBuffer.cpp',
'libANGLE/renderer/vulkan/SurfaceVk.cpp',
'libANGLE/renderer/vulkan/SurfaceVk.h',
'libANGLE/renderer/vulkan/SyncVk.cpp',
......
......@@ -333,6 +333,18 @@ TEST_P(SimpleOperationTest, DrawQuad)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Simple quad test with data in client memory, not vertex buffer.
TEST_P(SimpleOperationTest, DrawQuadFromClientMemory)
{
ANGLE_GL_PROGRAM(program, kBasicVertexShader, kGreenFragmentShader);
drawQuad(program.get(), "position", 0.5f, 1.0f, false);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Simple double quad test.
TEST_P(SimpleOperationTest, DrawQuadTwice)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment