{"action":"created","comment":{"url":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/comments/206799138","pull_request_review_id":142279480,"id":206799138,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwNjc5OTEzOA==","diff_hunk":"@@ -0,0 +1,310 @@\n+## Introduction\n+\n+Our program can now load multiple levels of detail for textures which fixes artifacts when rendering objects far away from the viewer. The image is now a lot smoother, however on closer inspection you will notice jagged saw-like patterns along the edges of drawn geometric shapes. This is especially visible in one of our early programs when we rendered a quad:\n+\n+![](/images/texcoord_visualization.png)\n+\n+This undesired effect is called \"aliasing\" and it's a result of a limited numbers of pixels that are available for rendering. Since there are no displays out there with unlimited resolution, it will be always visible to some extent. There's a number of ways to fix this and in this chapter we'll focus on one of the more popular ones: [Multisample anti-aliasing](https://en.wikipedia.org/wiki/Multisample_anti-aliasing) (MSAA).\n+\n+In ordinary rendering, the pixel color is determined based on a single sample point which in most cases is the center of target pixel on screen. If part of the drawn line passes through a certain pixel but doesn't cover the sample point, that pixel will be left blank, leading to the jagged \"staircase\" effect.\n+\n+![](/images/aliasing.png)\n+\n+What MSAA does is it uses multiple sample points per pixel (hence the name) to determine its final color. As one might expect, more samples lead to better results, however it is also more computationally expensive.\n+\n+![](/images/antialiasing.png)\n+\n+In our implementation, we will focus on using the maximum available sample count. Depending on your application this may not always be the best approach and it might be better to use less samples for the sake of higher performance if the final result meets your quality demands.\n+\n+\n+## Getting available sample count\n+\n+Let's start off by determining how many samples our hardware can use. Most modern GPUs support at least 8 samples but this number is not guaranteed to be the same everywhere. We'll keep track of it by adding a new class member:\n+\n+```c++\n+...\n+VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;\n+...\n+```\n+\n+By default we'll be using only one sample per pixel which is equivalent to no multisampling, in which case the final image will remain unchanged. The exact maximum number of samples can be extracted from `VkPhysicalDeviceProperties` associated with our selected physical device. We're using a depth buffer, so we have to take into account the sample count for both color and depth - the lower number will be the maximum we can support. Add a function that will fetch this information for us:\n+\n+```c++\n+VkSampleCountFlagBits getMaxUsableSampleCount() {\n+    VkPhysicalDeviceProperties physicalDeviceProperties;\n+    vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);\n+\n+    VkSampleCountFlags counts = std::min(physicalDeviceProperties.limits.framebufferColorSampleCounts, physicalDeviceProperties.limits.framebufferDepthSampleCounts);\n+    if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; }\n+    if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; }\n+    if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; }\n+    if (counts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; }\n+    if (counts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; }\n+    if (counts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; }\n+\n+    return VK_SAMPLE_COUNT_1_BIT;\n+}\n+```\n+\n+We will now use this function to set the `msaaSamples` variable during the physical device selection process. For this, we have to slightly modify the `pickPhysicalDevice` function:\n+\n+```c++\n+void pickPhysicalDevice() {\n+            ...\n+            if (isDeviceSuitable(device)) {\n+                physicalDevice = device;\n+                msaaSamples = getMaxUsableSampleCount();\n+                break;\n+            }\n+            ...\n+}\n+```\n+\n+Next, update `createImage` function to allow us to specify the number of used samples by adding a `numSamples` parameter - this will become important later:\n+\n+```c++\n+void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {\n+    ...\n+    imageInfo.samples = numSamples;\n+    ...\n+```\n+\n+For now, update all calls to this function using `VK_SAMPLE_COUNT_1_BIT` - we will be replacing this with proper values as we progress with implementation:\n+\n+```c++\n+createImage(swapChainExtent.width, swapChainExtent.height, 1, VK_SAMPLE_COUNT_1_BIT, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImages[i], depthImagesMemory[i]);\n+...\n+createImage(texWidth, texHeight, mipLevels, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);\n+```\n+\n+## Setting up render targets\n+\n+In MSAA, each pixel is sampled in an offscreen buffer which is then rendered to the screen. These new buffers are slightly different from regular images we've been rendering to - they have to be able to store more than one sample per pixel. Once a multisampled buffer is created, it has to be attached to the default framebuffer (which stores only a single sample per pixel). This is why we have to create additional render targets and modify our current drawing process. Add following class members:\n+\n+```c++\n+...\n+std::vector<VkImage> colorImages;\n+std::vector<VkDeviceMemory> colorImagesMemory;\n+std::vector<VkImageView> colorImagesView;\n+\n+std::vector<VkImage> depthMsaaImages;\n+std::vector<VkDeviceMemory> depthMsaaImagesMemory;\n+std::vector<VkImageView> depthMsaaImagesView;\n+...\n+```\n+\n+We will now create a multisampled color buffer. Same as in case of non-multisampled image, we'll be dealing with dedicated resources for each swapchain image. Add a `createColorResources` function and note that we're using `msaaSamples` here as a function parameter to `createImage`. We're also using only one mip level, since this is enforced by the Vulkan specification in case of images with more than one sample per pixel:\n+\n+```c++\n+void createColorResources() {\n+    VkFormat colorFormat = swapChainImageFormat;\n+\n+    colorImages.resize(swapChainImages.size());\n+    colorImagesMemory.resize(swapChainImages.size());\n+    colorImageViews.resize(swapChainImages.size());\n+\n+    for (size_t i = 0; i < swapChainImageViews.size(); i++) {\n+        createImage(swapChainExtent.width, swapChainExtent.height, 1, msaaSamples, colorFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, colorImages[i], colorImagesMemory[i]);\n+        colorImageViews[i] = createImageView(colorImages[i], colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);\n+\n+        transitionImageLayout(colorImages[i], colorFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1);\n+    }\n+}\n+```\n+\n+For consistency, call the function right before `createDepthResources`:\n+\n+```c++\n+void initVulkan() {\n+    ...\n+    createColorResources();\n+    createDepthResources();\n+    ...\n+}\n+```\n+\n+You may notice that the newly created color image uses a transition path from `VK_IMAGE_LAYOUT_UNDEFINED` to `VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL` which is a new case for us to handle. Let's update `transitionImageLayout` function to take this into account:\n+\n+```c++\n+void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels) {\n+    ...\n+    else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {\n+        barrier.srcAccessMask = 0;\n+        barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;\n+        sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;\n+        destinationStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n+    }\n+    else {\n+        throw std::invalid_argument(\"unsupported layout transition!\");\n+    }\n+    ...\n+}\n+```\n+\n+Now that we have multisampled color buffer in place it's time to take care of depth. Modify `createDepthResources` and add creation steps for a multisampled depth buffer:\n+\n+```c++\n+void createDepthResources() {\n+    ...\n+    depthMsaaImages.resize(swapChainImages.size());\n+    depthMsaaImagesMemory.resize(swapChainImages.size());\n+    depthMsaaImagesView.resize(swapChainImages.size());\n+    ...\n+\n+    for (size_t i = 0; i < swapChainImageViews.size(); i++) {\n+        ...\n+        createImage(swapChainExtent.width, swapChainExtent.height, 1, msaaSamples, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthMsaaImages[i], depthMsaaImagesMemory[i]);\n+        depthMsaaImagesView[i] = createImageView(depthMsaaImages[i], depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);\n+\n+        transitionImageLayout(depthMsaaImages[i], depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 1);\n+    }\n+}\n+```\n+\n+We have now created a couple of new Vulkan resources, so let's not forget to release them when necessary:\n+\n+```c++\n+void cleanupSwapChain() {\n+    for (size_t i = 0; i < swapChainImages.size(); i++) {\n+        vkDestroyImageView(device, colorImageViews[i], nullptr);\n+        vkDestroyImage(device, colorImages[i], nullptr);\n+        vkFreeMemory(device, colorImagesMemory[i], nullptr);\n+        vkDestroyImageView(device, depthMsaaImagesView[i], nullptr);\n+        vkDestroyImage(device, depthMsaaImages[i], nullptr);\n+        vkFreeMemory(device, depthMsaaImagesMemory[i], nullptr);\n+        ...\n+    }\n+    ...\n+}\n+```\n+\n+With only a few simple steps we created additional buffers and image views necessary for multsampling - it's now time to put it all together and see the results!\n+\n+## Adding new attachments\n+\n+Let's take care of the render pass first. Modify `createRenderPass` and update color and depth attachment creation info structs:\n+\n+```c++\n+void createRenderPass() {\n+    ...\n+    colorAttachment.samples = msaaSamples;\n+    colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n+    ...\n+    depthAttachment.samples = msaaSamples;\n+    ...\n+```\n+\n+Apart from the obvious change that tells both color and depth to use more samples, you'll notice an update to the `finalLayout` parameter for the color attachment. This is because a multisampled image is a fairly complex structure containing more information than a regular image. One of the consequences of this is that it cannot be processed by a sampler, so it cannot be drawn directly to the screen. For that reason, a multisampled image has to be translated (resolved) to a regular image before it can be used. This also applies to the depth buffer, so we need to define two additional attachments:\n+\n+```c++\n+    ...\n+    VkAttachmentDescription colorAttachmentResolve = {};\n+    colorAttachmentResolve.format = swapChainImageFormat;\n+    colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;\n+    colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n+    colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n+    colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n+    colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n+    colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n+    colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;\n+\n+    VkAttachmentDescription depthAttachmentResolve = {};\n+    depthAttachmentResolve.format = findDepthFormat();\n+    depthAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;\n+    depthAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n+    depthAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n+    depthAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n+    depthAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n+    depthAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n+    depthAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;\n+    ...\n+```\n+\n+The render pass now has to be instructed to perform color image resolution. Create a new attachment reference that will point to the color buffer which will serve as the resolve target:\n+\n+```c++\n+    ...\n+    VkAttachmentReference colorAttachmentResolveRef = {};\n+    colorAttachmentResolveRef.attachment = 2;\n+    colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n+    ...\n+```\n+\n+Set the `pResolveAttachments` subpass struct member to point to the newly created attachment reference. This is enough to let the render pass define a multisample resolve operation which will let us render the image to screen:\n+\n+```\n+    ...\n+    subpass.pResolveAttachments = &colorAttachmentResolveRef;","path":"10_Multisampling.md","position":237,"original_position":237,"commit_id":"b46f585d48648e99b01e8fab0a9cf47e2871de34","original_commit_id":"a7e3b67cac54555f39c3efa2e070b94901402482","user":{"login":"kondrak","id":423295,"node_id":"MDQ6VXNlcjQyMzI5NQ==","avatar_url":"https://avatars1.githubusercontent.com/u/423295?v=4","gravatar_id":"","url":"https://api.github.com/users/kondrak","html_url":"https://github.com/kondrak","followers_url":"https://api.github.com/users/kondrak/followers","following_url":"https://api.github.com/users/kondrak/following{/other_user}","gists_url":"https://api.github.com/users/kondrak/gists{/gist_id}","starred_url":"https://api.github.com/users/kondrak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kondrak/subscriptions","organizations_url":"https://api.github.com/users/kondrak/orgs","repos_url":"https://api.github.com/users/kondrak/repos","events_url":"https://api.github.com/users/kondrak/events{/privacy}","received_events_url":"https://api.github.com/users/kondrak/received_events","type":"User","site_admin":false},"body":"Whoops, you're right. There's in fact no need to create a msaa depth attachment, since even the subpass create info has no means of knowing about it and `pResoleAttachments` is for color only. I'll have it fixed.","created_at":"2018-08-01T08:44:52Z","updated_at":"2018-08-01T08:44:53Z","html_url":"https://github.com/Overv/VulkanTutorial/pull/101#discussion_r206799138","pull_request_url":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/101","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/comments/206799138"},"html":{"href":"https://github.com/Overv/VulkanTutorial/pull/101#discussion_r206799138"},"pull_request":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/101"}},"in_reply_to_id":206657830},"pull_request":{"url":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/101","id":203280919,"node_id":"MDExOlB1bGxSZXF1ZXN0MjAzMjgwOTE5","html_url":"https://github.com/Overv/VulkanTutorial/pull/101","diff_url":"https://github.com/Overv/VulkanTutorial/pull/101.diff","patch_url":"https://github.com/Overv/VulkanTutorial/pull/101.patch","issue_url":"https://api.github.com/repos/Overv/VulkanTutorial/issues/101","number":101,"state":"open","locked":false,"title":"Chapter on multisampling","user":{"login":"kondrak","id":423295,"node_id":"MDQ6VXNlcjQyMzI5NQ==","avatar_url":"https://avatars1.githubusercontent.com/u/423295?v=4","gravatar_id":"","url":"https://api.github.com/users/kondrak","html_url":"https://github.com/kondrak","followers_url":"https://api.github.com/users/kondrak/followers","following_url":"https://api.github.com/users/kondrak/following{/other_user}","gists_url":"https://api.github.com/users/kondrak/gists{/gist_id}","starred_url":"https://api.github.com/users/kondrak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kondrak/subscriptions","organizations_url":"https://api.github.com/users/kondrak/orgs","repos_url":"https://api.github.com/users/kondrak/repos","events_url":"https://api.github.com/users/kondrak/events{/privacy}","received_events_url":"https://api.github.com/users/kondrak/received_events","type":"User","site_admin":false},"body":"Here's the first draft of a multisampling chapter, complete with working code. I deliberately didn't delve too much into technical details about how the technique works, so I hope it's enough.","created_at":"2018-07-23T16:53:10Z","updated_at":"2018-08-01T08:44:53Z","closed_at":null,"merged_at":null,"merge_commit_sha":"45085d49a6c93e99166531fbf06c52b984845322","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/101/commits","review_comments_url":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/101/comments","review_comment_url":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Overv/VulkanTutorial/issues/101/comments","statuses_url":"https://api.github.com/repos/Overv/VulkanTutorial/statuses/b46f585d48648e99b01e8fab0a9cf47e2871de34","head":{"label":"kondrak:master","ref":"master","sha":"b46f585d48648e99b01e8fab0a9cf47e2871de34","user":{"login":"kondrak","id":423295,"node_id":"MDQ6VXNlcjQyMzI5NQ==","avatar_url":"https://avatars1.githubusercontent.com/u/423295?v=4","gravatar_id":"","url":"https://api.github.com/users/kondrak","html_url":"https://github.com/kondrak","followers_url":"https://api.github.com/users/kondrak/followers","following_url":"https://api.github.com/users/kondrak/following{/other_user}","gists_url":"https://api.github.com/users/kondrak/gists{/gist_id}","starred_url":"https://api.github.com/users/kondrak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kondrak/subscriptions","organizations_url":"https://api.github.com/users/kondrak/orgs","repos_url":"https://api.github.com/users/kondrak/repos","events_url":"https://api.github.com/users/kondrak/events{/privacy}","received_events_url":"https://api.github.com/users/kondrak/received_events","type":"User","site_admin":false},"repo":{"id":141620200,"node_id":"MDEwOlJlcG9zaXRvcnkxNDE2MjAyMDA=","name":"VulkanTutorial","full_name":"kondrak/VulkanTutorial","owner":{"login":"kondrak","id":423295,"node_id":"MDQ6VXNlcjQyMzI5NQ==","avatar_url":"https://avatars1.githubusercontent.com/u/423295?v=4","gravatar_id":"","url":"https://api.github.com/users/kondrak","html_url":"https://github.com/kondrak","followers_url":"https://api.github.com/users/kondrak/followers","following_url":"https://api.github.com/users/kondrak/following{/other_user}","gists_url":"https://api.github.com/users/kondrak/gists{/gist_id}","starred_url":"https://api.github.com/users/kondrak/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kondrak/subscriptions","organizations_url":"https://api.github.com/users/kondrak/orgs","repos_url":"https://api.github.com/users/kondrak/repos","events_url":"https://api.github.com/users/kondrak/events{/privacy}","received_events_url":"https://api.github.com/users/kondrak/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/kondrak/VulkanTutorial","description":"Tutorial for the Vulkan graphics and compute API","fork":true,"url":"https://api.github.com/repos/kondrak/VulkanTutorial","forks_url":"https://api.github.com/repos/kondrak/VulkanTutorial/forks","keys_url":"https://api.github.com/repos/kondrak/VulkanTutorial/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kondrak/VulkanTutorial/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kondrak/VulkanTutorial/teams","hooks_url":"https://api.github.com/repos/kondrak/VulkanTutorial/hooks","issue_events_url":"https://api.github.com/repos/kondrak/VulkanTutorial/issues/events{/number}","events_url":"https://api.github.com/repos/kondrak/VulkanTutorial/events","assignees_url":"https://api.github.com/repos/kondrak/VulkanTutorial/assignees{/user}","branches_url":"https://api.github.com/repos/kondrak/VulkanTutorial/branches{/branch}","tags_url":"https://api.github.com/repos/kondrak/VulkanTutorial/tags","blobs_url":"https://api.github.com/repos/kondrak/VulkanTutorial/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kondrak/VulkanTutorial/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kondrak/VulkanTutorial/git/refs{/sha}","trees_url":"https://api.github.com/repos/kondrak/VulkanTutorial/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kondrak/VulkanTutorial/statuses/{sha}","languages_url":"https://api.github.com/repos/kondrak/VulkanTutorial/languages","stargazers_url":"https://api.github.com/repos/kondrak/VulkanTutorial/stargazers","contributors_url":"https://api.github.com/repos/kondrak/VulkanTutorial/contributors","subscribers_url":"https://api.github.com/repos/kondrak/VulkanTutorial/subscribers","subscription_url":"https://api.github.com/repos/kondrak/VulkanTutorial/subscription","commits_url":"https://api.github.com/repos/kondrak/VulkanTutorial/commits{/sha}","git_commits_url":"https://api.github.com/repos/kondrak/VulkanTutorial/git/commits{/sha}","comments_url":"https://api.github.com/repos/kondrak/VulkanTutorial/comments{/number}","issue_comment_url":"https://api.github.com/repos/kondrak/VulkanTutorial/issues/comments{/number}","contents_url":"https://api.github.com/repos/kondrak/VulkanTutorial/contents/{+path}","compare_url":"https://api.github.com/repos/kondrak/VulkanTutorial/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kondrak/VulkanTutorial/merges","archive_url":"https://api.github.com/repos/kondrak/VulkanTutorial/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kondrak/VulkanTutorial/downloads","issues_url":"https://api.github.com/repos/kondrak/VulkanTutorial/issues{/number}","pulls_url":"https://api.github.com/repos/kondrak/VulkanTutorial/pulls{/number}","milestones_url":"https://api.github.com/repos/kondrak/VulkanTutorial/milestones{/number}","notifications_url":"https://api.github.com/repos/kondrak/VulkanTutorial/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kondrak/VulkanTutorial/labels{/name}","releases_url":"https://api.github.com/repos/kondrak/VulkanTutorial/releases{/id}","deployments_url":"https://api.github.com/repos/kondrak/VulkanTutorial/deployments","created_at":"2018-07-19T19:03:18Z","updated_at":"2018-08-01T08:09:58Z","pushed_at":"2018-08-01T08:09:56Z","git_url":"git://github.com/kondrak/VulkanTutorial.git","ssh_url":"git@github.com:kondrak/VulkanTutorial.git","clone_url":"https://github.com/kondrak/VulkanTutorial.git","svn_url":"https://github.com/kondrak/VulkanTutorial","homepage":"https://vulkan-tutorial.com","size":48407,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Overv:master","ref":"master","sha":"4eb7147a9896b49b018a8c6ecce2f50d13ab226f","user":{"login":"Overv","id":285063,"node_id":"MDQ6VXNlcjI4NTA2Mw==","avatar_url":"https://avatars3.githubusercontent.com/u/285063?v=4","gravatar_id":"","url":"https://api.github.com/users/Overv","html_url":"https://github.com/Overv","followers_url":"https://api.github.com/users/Overv/followers","following_url":"https://api.github.com/users/Overv/following{/other_user}","gists_url":"https://api.github.com/users/Overv/gists{/gist_id}","starred_url":"https://api.github.com/users/Overv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Overv/subscriptions","organizations_url":"https://api.github.com/users/Overv/orgs","repos_url":"https://api.github.com/users/Overv/repos","events_url":"https://api.github.com/users/Overv/events{/privacy}","received_events_url":"https://api.github.com/users/Overv/received_events","type":"User","site_admin":false},"repo":{"id":31612175,"node_id":"MDEwOlJlcG9zaXRvcnkzMTYxMjE3NQ==","name":"VulkanTutorial","full_name":"Overv/VulkanTutorial","owner":{"login":"Overv","id":285063,"node_id":"MDQ6VXNlcjI4NTA2Mw==","avatar_url":"https://avatars3.githubusercontent.com/u/285063?v=4","gravatar_id":"","url":"https://api.github.com/users/Overv","html_url":"https://github.com/Overv","followers_url":"https://api.github.com/users/Overv/followers","following_url":"https://api.github.com/users/Overv/following{/other_user}","gists_url":"https://api.github.com/users/Overv/gists{/gist_id}","starred_url":"https://api.github.com/users/Overv/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Overv/subscriptions","organizations_url":"https://api.github.com/users/Overv/orgs","repos_url":"https://api.github.com/users/Overv/repos","events_url":"https://api.github.com/users/Overv/events{/privacy}","received_events_url":"https://api.github.com/users/Overv/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/Overv/VulkanTutorial","description":"Tutorial for the Vulkan graphics and compute API","fork":false,"url":"https://api.github.com/repos/Overv/VulkanTutorial","forks_url":"https://api.github.com/repos/Overv/VulkanTutorial/forks","keys_url":"https://api.github.com/repos/Overv/VulkanTutorial/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Overv/VulkanTutorial/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Overv/VulkanTutorial/teams","hooks_url":"https://api.github.com/repos/Overv/VulkanTutorial/hooks","issue_events_url":"https://api.github.com/repos/Overv/VulkanTutorial/issues/events{/number}","events_url":"https://api.github.com/repos/Overv/VulkanTutorial/events","assignees_url":"https://api.github.com/repos/Overv/VulkanTutorial/assignees{/user}","branches_url":"https://api.github.com/repos/Overv/VulkanTutorial/branches{/branch}","tags_url":"https://api.github.com/repos/Overv/VulkanTutorial/tags","blobs_url":"https://api.github.com/repos/Overv/VulkanTutorial/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Overv/VulkanTutorial/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Overv/VulkanTutorial/git/refs{/sha}","trees_url":"https://api.github.com/repos/Overv/VulkanTutorial/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Overv/VulkanTutorial/statuses/{sha}","languages_url":"https://api.github.com/repos/Overv/VulkanTutorial/languages","stargazers_url":"https://api.github.com/repos/Overv/VulkanTutorial/stargazers","contributors_url":"https://api.github.com/repos/Overv/VulkanTutorial/contributors","subscribers_url":"https://api.github.com/repos/Overv/VulkanTutorial/subscribers","subscription_url":"https://api.github.com/repos/Overv/VulkanTutorial/subscription","commits_url":"https://api.github.com/repos/Overv/VulkanTutorial/commits{/sha}","git_commits_url":"https://api.github.com/repos/Overv/VulkanTutorial/git/commits{/sha}","comments_url":"https://api.github.com/repos/Overv/VulkanTutorial/comments{/number}","issue_comment_url":"https://api.github.com/repos/Overv/VulkanTutorial/issues/comments{/number}","contents_url":"https://api.github.com/repos/Overv/VulkanTutorial/contents/{+path}","compare_url":"https://api.github.com/repos/Overv/VulkanTutorial/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Overv/VulkanTutorial/merges","archive_url":"https://api.github.com/repos/Overv/VulkanTutorial/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Overv/VulkanTutorial/downloads","issues_url":"https://api.github.com/repos/Overv/VulkanTutorial/issues{/number}","pulls_url":"https://api.github.com/repos/Overv/VulkanTutorial/pulls{/number}","milestones_url":"https://api.github.com/repos/Overv/VulkanTutorial/milestones{/number}","notifications_url":"https://api.github.com/repos/Overv/VulkanTutorial/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Overv/VulkanTutorial/labels{/name}","releases_url":"https://api.github.com/repos/Overv/VulkanTutorial/releases{/id}","deployments_url":"https://api.github.com/repos/Overv/VulkanTutorial/deployments","created_at":"2015-03-03T17:45:42Z","updated_at":"2018-07-31T14:13:17Z","pushed_at":"2018-08-01T08:09:57Z","git_url":"git://github.com/Overv/VulkanTutorial.git","ssh_url":"git@github.com:Overv/VulkanTutorial.git","clone_url":"https://github.com/Overv/VulkanTutorial.git","svn_url":"https://github.com/Overv/VulkanTutorial","homepage":"https://vulkan-tutorial.com","size":48605,"stargazers_count":712,"watchers_count":712,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":113,"mirror_url":null,"archived":false,"open_issues_count":4,"license":null,"forks":113,"open_issues":4,"watchers":712,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/101"},"html":{"href":"https://github.com/Overv/VulkanTutorial/pull/101"},"issue":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/issues/101"},"comments":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/issues/101/comments"},"review_comments":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/101/comments"},"review_comment":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/pulls/101/commits"},"statuses":{"href":"https://api.github.com/repos/Overv/VulkanTutorial/statuses/b46f585d48648e99b01e8fab0a9cf47e2871de34"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"kondrak"}}