text: // Copyright 2017 Google Inc.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #include "application_sandbox/sample_application_framework/sample_application.h"
  #include "support/entry/entry.h"
  #include "vulkan_helpers/buffer_frame_data.h"
  #include "vulkan_helpers/helper_functions.h"
  #include "vulkan_helpers/vulkan_application.h"
  #include "vulkan_helpers/vulkan_model.h"
  #include "vulkan_helpers/vulkan_texture.h"
  
  #include <chrono>
  #include "mathfu/matrix.h"
  #include "mathfu/vector.h"
  
  using Mat44 = mathfu::Matrix<float, 4, 4>;
  using Vector4 = mathfu::Vector<float, 4>;
  
  namespace cube_model {
  #include "cube.obj.h"
  }
  const auto& cube_data = cube_model::model;
  
  uint32_t sparse_binding_vertex_shader[] =
  #include "sparse_binding.vert.spv"
      ;
  
  uint32_t sparse_binding_fragment_shader[] =
  #include "sparse_binding.frag.spv"
      ;
  
  namespace simple_texture {
  #include "star.png.h"
  }
  
  const auto& texture_data = simple_texture::texture;
  
  struct TexturedCubeFrameData {
    containers::unique_ptr<vulkan::VkCommandBuffer> command_buffer_;
    containers::unique_ptr<vulkan::VkFramebuffer> framebuffer_;
    containers::unique_ptr<vulkan::DescriptorSet> cube_descriptor_set_;
  };
  
  const size_t kSparseBindingBlockSize= 1024u;
  
  namespace {
    VkPhysicalDeviceFeatures enableSparseBindingFeature() {
        VkPhysicalDeviceFeatures features{0};
        features.sparseBinding = VK_TRUE;
        return features;
    }
  } // namespace
  
  // This creates an application with 16MB of image memory, and defaults
  // for host, and device buffer sizes.
  class TexturedCubeSample
      : public sample_application::Sample<TexturedCubeFrameData> {
   public:
    TexturedCubeSample(const entry::entry_data* data)
        : data_(data),
          Sample<TexturedCubeFrameData>(
              data->root_allocator, data, 1, 512, 1, 1,
              sample_application::SampleOptions().EnableSparseBinding(),
              enableSparseBindingFeature()),
          cube_(data->root_allocator, data->log.get(), cube_data),
          texture_(data->root_allocator, data->log.get(), texture_data,
                   kSparseBindingBlockSize) {}
    virtual void InitializeApplicationData(
        vulkan::VkCommandBuffer* initialization_buffer,
        size_t num_swapchain_images) override {
      cube_.InitializeData(app(), initialization_buffer);
      texture_.InitializeData(app(), initialization_buffer);
  
      cube_descriptor_set_layouts_[0] = {
          0,                                  // binding
          VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,  // descriptorType
          1,                                  // descriptorCount
          VK_SHADER_STAGE_VERTEX_BIT,         // stageFlags
          nullptr                             // pImmutableSamplers
      };
      cube_descriptor_set_layouts_[1] = {
          1,                                  // binding
          VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,  // descriptorType
          1,                                  // descriptorCount
          VK_SHADER_STAGE_VERTEX_BIT,         // stageFlags
          nullptr                             // pImmutableSamplers
      };
      cube_descriptor_set_layouts_[2] = {
          2,                             // binding
          VK_DESCRIPTOR_TYPE_SAMPLER,    // descriptorType
          1,                             // descriptorCount
          VK_SHADER_STAGE_FRAGMENT_BIT,  // stageFlags
          nullptr                        // pImmutableSamplers
      };
      cube_descriptor_set_layouts_[3] = {
          3,                                 // binding
          VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,  // descriptorType
          1,                                 // descriptorCount
          VK_SHADER_STAGE_FRAGMENT_BIT,      // stageFlags
          nullptr                            // pImmutableSamplers
      };
  
      sampler_ = containers::make_unique<vulkan::VkSampler>(
          data_->root_allocator,
          vulkan::CreateSampler(&app()->device(), VK_FILTER_LINEAR,
                                VK_FILTER_LINEAR));
  
      pipeline_layout_ = containers::make_unique<vulkan::PipelineLayout>(
          data_->root_allocator,
          app()->CreatePipelineLayout(
              {{cube_descriptor_set_layouts_[0], cube_descriptor_set_layouts_[1],
                cube_descriptor_set_layouts_[2],
                cube_descriptor_set_layouts_[3]}}));
  
      VkAttachmentReference color_attachment = {
          0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
  
      render_pass_ = containers::make_unique<vulkan::VkRenderPass>(
          data_->root_allocator,
          app()->CreateRenderPass(
              {{
                  0,                                         // flags
                  render_format(),                           // format
                  num_samples(),                             // samples
                  VK_ATTACHMENT_LOAD_OP_CLEAR,               // loadOp
                  VK_ATTACHMENT_STORE_OP_STORE,              // storeOp
                  VK_ATTACHMENT_LOAD_OP_DONT_CARE,           // stenilLoadOp
                  VK_ATTACHMENT_STORE_OP_DONT_CARE,          // stenilStoreOp
                  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,  // initialLayout
                  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL   // finalLayout
              }},  // AttachmentDescriptions
              {{
                  0,                                // flags
                  VK_PIPELINE_BIND_POINT_GRAPHICS,  // pipelineBindPoint
                  0,                                // inputAttachmentCount
                  nullptr,                          // pInputAttachments
                  1,                                // colorAttachmentCount
                  &color_attachment,                // colorAttachment
                  nullptr,                          // pResolveAttachments
                  nullptr,                          // pDepthStencilAttachment
                  0,                                // preserveAttachmentCount
                  nullptr                           // pPreserveAttachments
              }},                                   // SubpassDescriptions
              {}                                    // SubpassDependencies
              ));
  
      cube_pipeline_ = containers::make_unique<vulkan::VulkanGraphicsPipeline>(
          data_->root_allocator,
          app()->CreateGraphicsPipeline(pipeline_layout_.get(),
                                        render_pass_.get(), 0));
      cube_pipeline_->AddShader(VK_SHADER_STAGE_VERTEX_BIT, "main",
                                sparse_binding_vertex_shader);
      cube_pipeline_->AddShader(VK_SHADER_STAGE_FRAGMENT_BIT, "main",
                                sparse_binding_fragment_shader);
      cube_pipeline_->SetTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
      cube_pipeline_->SetInputStreams(&cube_);
      cube_pipeline_->SetViewport(viewport());
      cube_pipeline_->SetScissor(scissor());
      cube_pipeline_->SetSamples(num_samples());
      cube_pipeline_->AddAttachment();
      cube_pipeline_->Commit();
  
      camera_data_ = containers::make_unique<vulkan::BufferFrameData<CameraData>>(
          data_->root_allocator, app(), num_swapchain_images,
          VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
  
      model_data_ = containers::make_unique<vulkan::BufferFrameData<ModelData>>(
          data_->root_allocator, app(), num_swapchain_images,
          VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
  
      float aspect =
          (float)app()->swapchain().width() / (float)app()->swapchain().height();
      camera_data_->data().projection_matrix =
          Mat44::FromScaleVector(mathfu::Vector<float, 3>{1.0f, -1.0f, 1.0f}) *
          Mat44::Perspective(1.5708f, aspect, 0.1f, 100.0f);
  
      model_data_->data().transform = Mat44::FromTranslationVector(
          mathfu::Vector<float, 3>{0.0f, 0.0f, -3.0f});
    }
  
    virtual void InitializationComplete() override {
      texture_.InitializationComplete();
    }
  
    virtual void InitializeFrameData(
        TexturedCubeFrameData* frame_data,
        vulkan::VkCommandBuffer* initialization_buffer,
        size_t frame_index) override {
      frame_data->command_buffer_ =
          containers::make_unique<vulkan::VkCommandBuffer>(
              data_->root_allocator, app()->GetCommandBuffer());
  
      frame_data
          ->cube_descriptor_set_ = containers::make_unique<vulkan::DescriptorSet>(
          data_->root_allocator,
          app()->AllocateDescriptorSet({
              cube_descriptor_set_layouts_[0], cube_descriptor_set_layouts_[1],
              cube_descriptor_set_layouts_[2], cube_descriptor_set_layouts_[3],
          }));
  
      VkDescriptorBufferInfo buffer_infos[2] = {
          {
              camera_data_->get_buffer(),                       // buffer
              camera_data_->get_offset_for_frame(frame_index),  // offset
              camera_data_->size(),                             // range
          },
          {
              model_data_->get_buffer(),                       // buffer
              model_data_->get_offset_for_frame(frame_index),  // offset
              model_data_->size(),                             // range
          }};
  
      VkDescriptorImageInfo sampler_info = {
          *sampler_,                 // sampler
          VK_NULL_HANDLE,            // imageView
          VK_IMAGE_LAYOUT_UNDEFINED  //  imageLayout
      };
      VkDescriptorImageInfo texture_info = {
          VK_NULL_HANDLE,                            // sampler
          texture_.view(),                           // imageView
          VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,  // imageLayout
      };
  
      VkWriteDescriptorSet writes[3] = {
          {
              VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,  // sType
              nullptr,                                 // pNext
              *frame_data->cube_descriptor_set_,       // dstSet
              0,                                       // dstbinding
              0,                                       // dstArrayElement
              2,                                       // descriptorCount
              VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,       // descriptorType
              nullptr,                                 // pImageInfo
              &buffer_infos[0],                        // pBufferInfo
              nullptr,                                 // pTexelBufferView
          },
          {
              VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,  // sType
              nullptr,                                 // pNext
              *frame_data->cube_descriptor_set_,       // dstSet
              2,                                       // dstbinding
              0,                                       // dstArrayElement
              1,                                       // descriptorCount
              VK_DESCRIPTOR_TYPE_SAMPLER,              // descriptorType
              &sampler_info,                           // pImageInfo
              nullptr,                                 // pBufferInfo
              nullptr,                                 // pTexelBufferView
          },
          {
              VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,  // sType
              nullptr,                                 // pNext
              *frame_data->cube_descriptor_set_,       // dstSet
              3,                                       // dstbinding
              0,                                       // dstArrayElement
              1,                                       // descriptorCount
              VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,        // descriptorType
              &texture_info,                           // pImageInfo
              nullptr,                                 // pBufferInfo
              nullptr,                                 // pTexelBufferView
          },
      };
  
      app()->device()->vkUpdateDescriptorSets(app()->device(), 3, writes, 0,
                                              nullptr);
  
      ::VkImageView raw_view = color_view(frame_data);
  
      // Create a framebuffer with depth and image attachments
      VkFramebufferCreateInfo framebuffer_create_info{
          VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,  // sType
          nullptr,                                    // pNext
          0,                                          // flags
          *render_pass_,                              // renderPass
          1,                                          // attachmentCount
          &raw_view,                                  // attachments
          app()->swapchain().width(),                 // width
          app()->swapchain().height(),                // height
          1                                           // layers
      };
  
      ::VkFramebuffer raw_framebuffer;
      app()->device()->vkCreateFramebuffer(
          app()->device(), &framebuffer_create_info, nullptr, &raw_framebuffer);
      frame_data->framebuffer_ = containers::make_unique<vulkan::VkFramebuffer>(
          data_->root_allocator,
          vulkan::VkFramebuffer(raw_framebuffer, nullptr, &app()->device()));
  
      (*frame_data->command_buffer_)
          ->vkBeginCommandBuffer((*frame_data->command_buffer_),
                                 &sample_application::kBeginCommandBuffer);
      vulkan::VkCommandBuffer& cmdBuffer = (*frame_data->command_buffer_);
  
      VkClearValue clear;
      vulkan::MemoryClear(&clear);
  
      VkRenderPassBeginInfo pass_begin = {
          VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,  // sType
          nullptr,                                   // pNext
          *render_pass_,                             // renderPass
          *frame_data->framebuffer_,                 // framebuffer
          {{0, 0},
           {app()->swapchain().width(),
            app()->swapchain().height()}},  // renderArea
          1,                                // clearValueCount
          &clear                            // clears
      };
  
      cmdBuffer->vkCmdBeginRenderPass(cmdBuffer, &pass_begin,
                                      VK_SUBPASS_CONTENTS_INLINE);
  
      cmdBuffer->vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
                                   *cube_pipeline_);
      cmdBuffer->vkCmdBindDescriptorSets(
          cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
          ::VkPipelineLayout(*pipeline_layout_), 0, 1,
          &frame_data->cube_descriptor_set_->raw_set(), 0, nullptr);
      cube_.Draw(&cmdBuffer);
      cmdBuffer->vkCmdEndRenderPass(cmdBuffer);
  
      (*frame_data->command_buffer_)
          ->vkEndCommandBuffer(*frame_data->command_buffer_);
    }
  
    virtual void Update(float time_since_last_render) override {
      model_data_->data().transform =
          model_data_->data().transform *
          Mat44::FromRotationMatrix(
              Mat44::RotationX(3.14f * time_since_last_render) *
              Mat44::RotationY(3.14f * time_since_last_render * 0.5f));
    }
    virtual void Render(vulkan::VkQueue* queue, size_t frame_index,
                        TexturedCubeFrameData* frame_data) override {
      // Update our uniform buffers.
      camera_data_->UpdateBuffer(queue, frame_index);
      model_data_->UpdateBuffer(queue, frame_index);
  
      VkSubmitInfo init_submit_info{
          VK_STRUCTURE_TYPE_SUBMIT_INFO,  // sType
          nullptr,                        // pNext
          0,                              // waitSemaphoreCount
          nullptr,                        // pWaitSemaphores
          nullptr,                        // pWaitDstStageMask,
          1,                              // commandBufferCount
          &(frame_data->command_buffer_->get_command_buffer()),
          0,       // signalSemaphoreCount
          nullptr  // pSignalSemaphores
      };
  
      app()->render_queue()->vkQueueSubmit(app()->render_queue(), 1,
                                           &init_submit_info,
                                           static_cast<VkFence>(VK_NULL_HANDLE));
    }
  
   private:
    struct CameraData {
      Mat44 projection_matrix;
    };
  
    struct ModelData {
      Mat44 transform;
    };
  
    const entry::entry_data* data_;
    containers::unique_ptr<vulkan::PipelineLayout> pipeline_layout_;
    containers::unique_ptr<vulkan::VulkanGraphicsPipeline> cube_pipeline_;
    containers::unique_ptr<vulkan::VkRenderPass> render_pass_;
    VkDescriptorSetLayoutBinding cube_descriptor_set_layouts_[4];
    vulkan::VulkanModel cube_;
    vulkan::VulkanTexture texture_;
    containers::unique_ptr<vulkan::VkSampler> sampler_;
  
    containers::unique_ptr<vulkan::BufferFrameData<CameraData>> camera_data_;
    containers::unique_ptr<vulkan::BufferFrameData<ModelData>> model_data_;
  };
  
  int main_entry(const entry::entry_data* data) {
    data->log->LogInfo("Application Startup");
    TexturedCubeSample sample(data);
    sample.Initialize();
  
    while (!sample.should_exit()) {
      sample.ProcessFrame();
    }
    sample.WaitIdle();
  
    data->log->LogInfo("Application Shutdown");
    return 0;
  }
  