Commit 7f6136bf authored by Shu's avatar Shu 🍠
Browse files

Vulkan MSAA

parent 94650837
......@@ -6,12 +6,14 @@ Work in progress galaxy down to atom (mostly centimeter) online voxel game
- [About The Project](#about-the-project)
- [Built With](#built-with)
- [Getting Started](#getting-started)
- [Run it](#run-it)
- [Prerequisites](#prerequisites)
- [Build](#build)
- [Usage](#usage)
- [Build](#build)
- [Prerequisites](#prerequisites-1)
- [Optionally](#optionally)
- [Installation](#installation)
- [Usage](#usage)
- [Additionally](#additionally)
- [RoadMap](#roadmap)
- [License](#license)
- [Contact](#contact)
......@@ -35,16 +37,30 @@ Experimental project using OpenGL and Vulkan.
<!-- GETTING STARTED -->
## Getting Started
To get a local copy up and running, follow these simple steps.
## Run it
Get a release compatible with your system window library and processor capabilities, or [build](#build) it
### Prerequisites
* OpenGL or Vulkan driver
#### Build
### Usage
```sh
./univerxel
```
Edit `config.toml`
<!-- MAYBE: config description -->
Profit !
## Build
To get a local copy up and running, follow these simple steps.
### Prerequisites
* C++17
* CMake 3.11
......@@ -53,8 +69,9 @@ To get a local copy up and running, follow these simple steps.
#### Optionally
* Python: utility scripts
* Tracy v0.7: profiling
* glslc: compile vk shaders
* [Tracy](https://github.com/wolfpld/tracy) v0.7: profiling
* [glslc](https://github.com/KhronosGroup/glslang): build vk shaders
* [Compressonator](https://github.com/GPUOpen-Tools/compressonator): build textures
### Installation
......@@ -68,23 +85,47 @@ mkdir build && cd build
```
3. Build CMake
```sh
cmake ..
cmake <options> ..
```
4. Build Make
CMake options: `-DKEY=VAL`
Key | Usage | Default
--- | --- | ---
SIMD_LEVEL | SIMD processor acceleration (sse2, sse4.1, avx2, avx512f) | `avx2`
USE_FMA | Fast math | `1`
CMAKE_BUILD_TYPE | Level of optimization | `Release`
PROFILING | Tracy profiling | `0`
LOG_DEBUG | Debug logs | `0`
LOG_TRACE | Trace logs | `0`
1. Build Make
```sh
make
make -j<process count> <target>
```
Target | Description
--- | ---
univerxel | All in one
univerxel-server | Standalone server
univerxel-client | Light client
docs | Doxygen documentation
<!-- USAGE EXAMPLES -->
## Usage
#### Additionally
5. Recompile Vulkan shaders
```sh
./univerxel
cd resource/shaders-src
./compile.sh # Use glslc
GLSL="glslValidator -V" ./compile.sh # Other glslang compiler
```
Profit !
6. Rebuild textures
```sh
cd resource/textures-src
./merge <args> # combine grey images to RGBA
Compressonator -fd BC3 -mipsize <size> <in> <out>
```
<!-- ROADMAP -->
......
......@@ -13,7 +13,7 @@ Client::Client(config::client::options& options): options(options) { }
Client::~Client() { }
void Client::run(server_handle* const localHandle) {
if (!render::Load(window, options.preferVulkan, options.renderer))
if (!render::Load(window, options.preferVulkan, options.renderer, options.window.samples))
return;
window.setTargetFPS(options.window.targetFPS);
......
......@@ -72,11 +72,16 @@ public:
};
struct requirement: properties {
requirement(const properties& props, Layout layout, Usage usage, Aspect aspect,
bool optimal = true): properties(props), layout(layout), usage(usage),
aspect(aspect), optimal(optimal) { }
int samples = 1, bool optimal = true): properties(props), layout(layout),
usage(usage), aspect(aspect), samples(samples), optimal(optimal)
{
assert(samples > 0 && (ceil(log2(n)) == floor(log2(n))) && "Samples must be pow2");
}
Layout layout;
Usage usage;
Aspect aspect;
//NOTE: matches VkSampleCountFlagBits
int samples;
bool optimal;
static requirement Texture(const properties &props) {
......
......@@ -63,11 +63,11 @@ void framebuffer_size_callback(GLFWwindow *, int width, int height) {
glViewport(0, 0, width, height);
}
bool Renderer::Load(Window& window, const renderOptions& opt) {
bool Renderer::Load(Window& window, const renderOptions& opt, int samples) {
Window::CreateInfo windowInfo;
windowInfo.pfnResize = framebuffer_size_callback;
windowInfo.client = {Window::CreateInfo::Client::Type::GL, GL_MAJOR, GL_MINOR};
windowInfo.samples = 1;
windowInfo.samples = samples;
if (!window.create(windowInfo))
return false;
......
......@@ -15,7 +15,7 @@ class Renderer final: public render::Renderer {
public:
virtual ~Renderer();
static bool Load(Window& window, const renderOptions& options);
static bool Load(Window& window, const renderOptions& options, int samples);
glm::vec3 FogColor;
GLfloat FogDepth;
......
......@@ -5,20 +5,20 @@
namespace render {
bool Load(Window& window, bool preferVulkan, const renderOptions& options) {
bool Load(Window& window, bool preferVulkan, const renderOptions& options, int samples) {
if(!preferVulkan) {
LOG_T("Trying OpenGL");
if(gl::Renderer::Load(window, options))
if(gl::Renderer::Load(window, options, samples))
return true;
window.destroy();
}
LOG_T("Trying Vulkan");
if(vk::Renderer::Load(window, options))
if(vk::Renderer::Load(window, options, samples))
return true;
window.destroy();
if(preferVulkan) {
LOG_I("Fallback to OpenGL");
if(gl::Renderer::Load(window, options))
if(gl::Renderer::Load(window, options, samples))
return true;
}
LOG_E("No available graphics library");
......
......@@ -4,5 +4,5 @@ class Window;
namespace render {
struct renderOptions;
bool Load(Window& window, bool preferVulkan, const renderOptions& options);
bool Load(Window& window, bool preferVulkan, const renderOptions& options, int samples);
}
\ No newline at end of file
......@@ -243,6 +243,12 @@ void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLay
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
} else {
FATAL("Unsupported layout transition!");
}
......
......@@ -34,6 +34,9 @@ CommandCenter::CommandCenter(VkDevice device, const PhysicalDeviceInfo &info, co
FATAL("Failed to create texture sampler!");
}
}
colorFormat = info.getSurfaceFormat().format;
colorSamples = info.samples;
LOG_D("Samples: " << colorSamples);
depthFormat = info.findDepthFormat();
}
CommandCenter::~CommandCenter() {
......@@ -46,13 +49,25 @@ CommandCenter::~CommandCenter() {
void CommandCenter::allocate(const std::vector<VkImageView>& views, const Pipeline& pipe, VkExtent2D extent, const renderOptions& opt) {
assert(freed);
if (colorSamples > 1) {
colorbuffer = Image::Create(Image::requirement({{extent.height, extent.width}, 1, (Image::Format)colorFormat},
Image::Layout::COLOR_ATTACHMENT, Image::Usage::COLOR_ATTACHMENT | Image::Usage::TRANSIENT_ATTACHMENT,
Image::Aspect::COLOR, colorSamples));
}
depthbuffer = Image::Create(Image::requirement({{extent.height, extent.width}, 1, (Image::Format)depthFormat},
Image::Layout::DEPTH_STENCIL_ATTACHMENT, Image::Usage::DEPTH_STENCIL_ATTACHMENT, Image::Aspect::DEPTH));
Image::Layout::DEPTH_STENCIL_ATTACHMENT, Image::Usage::DEPTH_STENCIL_ATTACHMENT,
Image::Aspect::DEPTH, colorSamples));
framebuffers.resize(views.size());
for (size_t i = 0; i < views.size(); i++) {
std::array<VkImageView, 2> attachments = { views[i], depthbuffer->getView() };
std::vector<VkImageView> attachments;
if (colorSamples > 1) {
attachments = {colorbuffer->getView(), depthbuffer->getView(), views[i]};
} else {
attachments = {views[i], depthbuffer->getView()};
}
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
......@@ -198,6 +213,7 @@ void CommandCenter::free() {
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
colorbuffer.reset();
depthbuffer.reset();
for (size_t i = 0; i < framebuffers.size(); i++) {
......@@ -216,6 +232,7 @@ void CommandCenter::updateUBO(uint32_t idx) {
auto currentTime = std::chrono::high_resolution_clock::now();
float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();
buffer::vk::UniformBufferObject ubo{};
time = 0.1;
ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
ubo.proj = proj;
......
......@@ -25,6 +25,10 @@ private:
VkDevice device;
std::vector<VkFramebuffer> framebuffers;
VkFormat colorFormat;
VkSampleCountFlagBits colorSamples;
std::unique_ptr<Image> colorbuffer;
VkFormat depthFormat;
std::unique_ptr<Image> depthbuffer;
......
......@@ -24,12 +24,26 @@ struct QueueFamilyIndices {
};
struct PhysicalDeviceInfo {
PhysicalDeviceInfo() {}
PhysicalDeviceInfo(GLFWwindow *window, VkPhysicalDevice device, VkSurfaceKHR surface):
PhysicalDeviceInfo(GLFWwindow *window, VkPhysicalDevice device, VkSurfaceKHR surface, int targetSamples):
window(window), device(device), surface(surface),
swapDetails(SwapChainSupportDetails::Query(device, surface)), queueIndices(QueueFamilyIndices::Query(device, surface))
{
vkGetPhysicalDeviceProperties(device, &properties);
vkGetPhysicalDeviceFeatures(device, &features);
samples = [&] {
if (targetSamples < 0)
targetSamples = 4;
VkSampleCountFlags counts = properties.limits.framebufferColorSampleCounts & properties.limits.framebufferDepthSampleCounts;
//MAYBE: properties.limits.framebufferStencilSampleCounts
for (auto sample: {VK_SAMPLE_COUNT_64_BIT, VK_SAMPLE_COUNT_32_BIT, VK_SAMPLE_COUNT_16_BIT,
VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_2_BIT
}) {
if (targetSamples >= sample && (counts & sample))
return sample;
}
return VK_SAMPLE_COUNT_1_BIT;
}();
}
VkSurfaceFormatKHR getSurfaceFormat() const;
......@@ -44,6 +58,7 @@ struct PhysicalDeviceInfo {
GLFWwindow *window;
VkPhysicalDevice device = VK_NULL_HANDLE;
VkSurfaceKHR surface;
VkSampleCountFlagBits samples;
SwapChainSupportDetails swapDetails;
QueueFamilyIndices queueIndices;
VkPhysicalDeviceProperties properties;
......
......@@ -13,16 +13,18 @@ constexpr auto BLENDING = true;
using namespace render::vk;
Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const renderOptions &options): device(device) {
const auto hasSamples = info.samples > 1;
{ // Render pass
VkAttachmentDescription colorAttachment{};
colorAttachment.format = info.getSurfaceFormat().format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.samples = info.samples;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
colorAttachment.finalLayout = hasSamples ?
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0; //TODO: layouts bind
......@@ -30,7 +32,7 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render
VkAttachmentDescription depthAttachment{};
depthAttachment.format = info.findDepthFormat();
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachment.samples = info.samples;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
......@@ -42,6 +44,20 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render
depthAttachmentRef.attachment = 1;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentDescription colorAttachmentResolve{};
colorAttachmentResolve.format = colorAttachment.format;
colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentResolveRef{};
colorAttachmentResolveRef.attachment = 2;
colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
//TODO: subpasses (world, entities, colors, sky, ui)
VkSubpassDescription subpass{};
......@@ -49,6 +65,9 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
subpass.pDepthStencilAttachment = &depthAttachmentRef;
if (hasSamples) {
subpass.pResolveAttachments = &colorAttachmentResolveRef;
}
VkSubpassDependency dependency{};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
......@@ -60,7 +79,10 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
std::array<VkAttachmentDescription, 2> attachments = {colorAttachment, depthAttachment};
std::vector<VkAttachmentDescription> attachments = {colorAttachment, depthAttachment};
if (hasSamples) {
attachments.push_back(colorAttachmentResolve);
}
renderPassInfo.attachmentCount = attachments.size();
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.subpassCount = 1;
......@@ -190,11 +212,12 @@ Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const render
rasterizer.depthBiasClamp = 0.0f;
rasterizer.depthBiasSlopeFactor = 0.0f;
VkPipelineMultisampleStateCreateInfo multisampling{}; //TODO: multisampling NOTE: requires gpu feature
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling.minSampleShading = 1.0f;
multisampling.rasterizationSamples = info.samples;
//MAYBE: add option
multisampling.sampleShadingEnable = hasSamples;
multisampling.minSampleShading = .2f;
multisampling.pSampleMask = nullptr;
multisampling.alphaToCoverageEnable = VK_FALSE;
multisampling.alphaToOneEnable = VK_FALSE;
......
......@@ -155,11 +155,11 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugValidationCallback(VkDebugUtilsMessageSeveri
return VK_FALSE;
}
bool Renderer::Load(Window& window, const renderOptions& opt) {
bool Renderer::Load(Window& window, const renderOptions& opt, int samples) {
Window::CreateInfo windowInfo;
windowInfo.pfnResize = on_resize_callback;
windowInfo.client = {Window::CreateInfo::Client::Type::VK, 0, 0};
windowInfo.samples = 1;
windowInfo.samples = -1;
if (!window.create(windowInfo))
return false;
......@@ -289,7 +289,7 @@ bool Renderer::Load(Window& window, const renderOptions& opt) {
uint bestScore = 0;
for(const auto& device: devices) {
uint score = 1;
auto infos = PhysicalDeviceInfo(window.getPtr(), device, surface);
auto infos = PhysicalDeviceInfo(window.getPtr(), device, surface, samples);
{
uint32_t availableExtensionsCount;
......@@ -321,6 +321,7 @@ bool Renderer::Load(Window& window, const renderOptions& opt) {
score += 10000;
score += infos.properties.limits.maxImageDimension2D;
score += infos.samples * 50;
if (infos.features.geometryShader)
score += 2000;
......
......@@ -13,7 +13,7 @@ class Renderer final: public render::Renderer {
public:
virtual ~Renderer();
static bool Load(Window& window, const renderOptions& options);
static bool Load(Window& window, const renderOptions& options, int samples);
void loadUI(Window &) override;
......
......@@ -30,7 +30,7 @@ memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags pro
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
info.usage = static_cast<VkImageUsageFlags>(req.usage);
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.samples = static_cast<VkSampleCountFlagBits>(req.samples);
info.flags = 0;
if (data) {
info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
......@@ -140,6 +140,10 @@ std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sa
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = props.mipmap ? header.mipmapLevels : 0.0f;
if (props.mipmap && header.mipmapLevels <= 1) {
LOG_D("Sampler requires mipmap but image does not");
}
VkSampler sampler;
if (vkCreateSampler(device, &samplerInfo, ALLOC, &sampler) != VK_SUCCESS) {
FATAL("Failed to create texture sampler!");
......
......@@ -23,10 +23,10 @@ public:
LOG_E("Config file " << path << " not found. Creating default");
return toml::table();
}();
if (config["server"]["enabled"].value_or(false)) {
if (config["server"]["enabled"].value_or(true)) {
_server = server::options(config["server"]);
}
if(config["client"]["enabled"].value_or(false)) {
if(config["client"]["enabled"].value_or(true)) {
_client = client::options(config["client"]);
}
}
......
Supports Markdown
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