Browse Source

More work on Vulkan renderer

rdb 10 years ago
parent
commit
7225a2768b

+ 1 - 0
panda/src/gobj/shader.h

@@ -53,6 +53,7 @@ PUBLISHED:
     SL_Cg,
     SL_GLSL,
     SL_HLSL,
+    SL_SPIR_V,
   };
 
   enum ShaderType {

+ 4 - 0
panda/src/vulkandisplay/config_vulkandisplay.cxx

@@ -15,6 +15,8 @@
 #include "vulkanGraphicsPipe.h"
 #include "vulkanGraphicsStateGuardian.h"
 #include "vulkanGraphicsWindow.h"
+#include "vulkanShaderContext.h"
+#include "vulkanVertexBufferContext.h"
 #include "graphicsPipeSelection.h"
 #include "dconfig.h"
 #include "pandaSystem.h"
@@ -45,6 +47,8 @@ init_libvulkandisplay() {
   VulkanGraphicsPipe::init_type();
   VulkanGraphicsStateGuardian::init_type();
   VulkanGraphicsWindow::init_type();
+  VulkanShaderContext::init_type();
+  VulkanVertexBufferContext::init_type();
 
   GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
   selection->add_pipe_type(VulkanGraphicsPipe::get_class_type(),

+ 2 - 0
panda/src/vulkandisplay/p3vulkandisplay_composite1.cxx

@@ -2,3 +2,5 @@
 #include "vulkanGraphicsPipe.cxx"
 #include "vulkanGraphicsStateGuardian.cxx"
 #include "vulkanGraphicsWindow.cxx"
+#include "vulkanShaderContext.cxx"
+#include "vulkanVertexBufferContext.cxx"

+ 34 - 2
panda/src/vulkandisplay/vulkanGraphicsPipe.cxx

@@ -152,7 +152,7 @@ VulkanGraphicsPipe() {
   // Query memory properties.
   vkGetPhysicalDeviceMemoryProperties(_gpu, &_memory_properties);
 
-  // Query queue information.  Currently unused, but keeps validator happy.
+  // Query queue information, used by find_queue_family_for_surface.
   uint32_t num_families;
   vkGetPhysicalDeviceQueueFamilyProperties(_gpu, &num_families, NULL);
   _queue_families.resize(num_families);
@@ -170,6 +170,7 @@ VulkanGraphicsPipe::
 
 /**
  * Finds the index of the memory type that fits the given requirements.
+ * @return true if a matching memory type was found
  */
 bool VulkanGraphicsPipe::
 find_memory_type(uint32_t &type_index, uint32_t type_bits, VkFlags required_flags) const {
@@ -188,6 +189,27 @@ find_memory_type(uint32_t &type_index, uint32_t type_bits, VkFlags required_flag
   return false;
 }
 
+/**
+ * Finds the index of the queue family capable of presenting to the given
+ * surface that fits the given requirements.
+ * @return true if a matching queue family was found
+ */
+bool VulkanGraphicsPipe::
+find_queue_family_for_surface(uint32_t &queue_family_index, VkSurfaceKHR surface, VkFlags required_flags) const {
+  // Iterate over each queue to learn whether it supports presenting.
+  for (uint32_t i = 0; i < _queue_families.size(); ++i) {
+    VkBool32 supports_present;
+    vkGetPhysicalDeviceSurfaceSupportKHR(_gpu, i, surface, &supports_present);
+
+    if ((_queue_families[i].queueFlags & required_flags) == required_flags) {
+      queue_family_index = i;
+      return true;
+    }
+  }
+
+  return false;
+}
+
 /**
  * Returns the name of the rendering interface associated with this
  * GraphicsPipe.  This is used to present to the user to allow him/her to
@@ -262,5 +284,15 @@ make_output(const string &name,
  */
 PT(GraphicsStateGuardian) VulkanGraphicsPipe::
 make_callback_gsg(GraphicsEngine *engine) {
-  return new VulkanGraphicsStateGuardian(engine, this, NULL);
+  // For now, grab the first queue family that can supports graphics commands.
+  uint32_t i;
+  for (i = 0; i < _queue_families.size(); ++i) {
+    if (_queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+      break;
+    }
+  }
+  if (i == _queue_families.size()) {
+    return NULL;
+  }
+  return new VulkanGraphicsStateGuardian(engine, this, NULL, i);
 }

+ 5 - 3
panda/src/vulkandisplay/vulkanGraphicsPipe.h

@@ -17,8 +17,6 @@
 #include "config_vulkandisplay.h"
 #include "graphicsOutput.h"
 
-#include <vulkan/vulkan.h>
-
 #ifdef _WIN32
 #include "winGraphicsPipe.h"
 typedef WinGraphicsPipe BaseGraphicsPipe;
@@ -36,7 +34,11 @@ public:
   VulkanGraphicsPipe();
   virtual ~VulkanGraphicsPipe();
 
-  bool find_memory_type(uint32_t &type_index, uint32_t type_bits, VkFlags required_flags) const;
+  bool find_memory_type(uint32_t &type_index, uint32_t type_bits,
+                        VkFlags required_flags) const;
+  bool find_queue_family_for_surface(uint32_t &queue_family_index,
+                                     VkSurfaceKHR surface,
+                                     VkFlags required_flags) const;
 
   virtual string get_interface_name() const;
   static PT(GraphicsPipe) pipe_constructor();

+ 665 - 10
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -12,21 +12,27 @@
  */
 
 #include "vulkanGraphicsStateGuardian.h"
+#include "vulkanVertexBufferContext.h"
 #include "standardMunger.h"
 
 TypeHandle VulkanGraphicsStateGuardian::_type_handle;
 
 /**
- *
+ * Creates a Vulkan device and queue.
  */
 VulkanGraphicsStateGuardian::
 VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
-                            VulkanGraphicsStateGuardian *share_with) :
+                            VulkanGraphicsStateGuardian *share_with,
+                            uint32_t queue_family_index) :
   GraphicsStateGuardian(CS_default, engine, pipe),
   _device(VK_NULL_HANDLE),
   _queue(VK_NULL_HANDLE),
   _cmd_pool(VK_NULL_HANDLE),
-  _cmd(VK_NULL_HANDLE)
+  _cmd(VK_NULL_HANDLE),
+  _render_pass(VK_NULL_HANDLE),
+  _pipeline_cache(VK_NULL_HANDLE),
+  _pipeline_layout(VK_NULL_HANDLE),
+  _default_sc(NULL)
 {
   const char *const layers[] = {
     "VK_LAYER_LUNARG_standard_validation",
@@ -36,12 +42,13 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
     "VK_KHR_swapchain",
   };
 
+  // Create a queue in the given queue family.
   const float queue_priorities[1] = {0.0f};
   VkDeviceQueueCreateInfo queue_info;
   queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
   queue_info.pNext = NULL;
   queue_info.flags = 0;
-  queue_info.queueFamilyIndex = 0;
+  queue_info.queueFamilyIndex = queue_family_index;
   queue_info.queueCount = 1;
   queue_info.pQueuePriorities = queue_priorities;
 
@@ -64,14 +71,15 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
     return;
   }
 
-  vkGetDeviceQueue(_device, 0, 0, &_queue);
+  vkGetDeviceQueue(_device, queue_family_index, 0, &_queue);
 
   // Create a command pool to allocate command buffers from.
   VkCommandPoolCreateInfo pool_info;
   pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
   pool_info.pNext = NULL;
-  pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
-  pool_info.queueFamilyIndex = 0;
+  pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
+                    VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+  pool_info.queueFamilyIndex = queue_family_index;
 
   err = vkCreateCommandPool(_device, &pool_info, NULL, &_cmd_pool);
   if (err) {
@@ -79,6 +87,63 @@ VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
     return;
   }
 
+  // Create a pipeline cache, which may help with performance.
+  VkPipelineCacheCreateInfo cache_info;
+  cache_info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+  cache_info.pNext = NULL;
+  cache_info.flags = 0;
+  cache_info.initialDataSize = 0;
+  cache_info.pInitialData = NULL;
+
+  err = vkCreatePipelineCache(_device, &cache_info, NULL, &_pipeline_cache);
+  if (err) {
+    // This isn't a fatal error, since we don't strictly need the cache.
+    vulkan_error(err, "Failed to create pipeline cache");
+  }
+
+  // Create a descriptor set layout.
+  VkDescriptorSetLayoutBinding layout_binding;
+  layout_binding.binding = 0;
+  layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+  layout_binding.descriptorCount = 1;
+  layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
+  layout_binding.pImmutableSamplers = NULL;
+
+  VkDescriptorSetLayoutCreateInfo set_info;
+  set_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+  set_info.pNext = NULL;
+  set_info.flags = 0;
+  set_info.bindingCount = 1;
+  set_info.pBindings = &layout_binding;
+
+  VkDescriptorSetLayout desc_set_layout;
+  err = vkCreateDescriptorSetLayout(_device, &set_info, NULL, &desc_set_layout);
+  if (err) {
+    vulkan_error(err, "Failed to create descriptor set layout");
+  }
+
+  // Create a pipeline layout.  We'll do that here for now.
+  VkPushConstantRange range;
+  range.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+  range.offset = 0;
+  range.size = 64;
+
+  VkPipelineLayoutCreateInfo layout_info;
+  layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+  layout_info.pNext = NULL;
+  layout_info.flags = 0;
+  layout_info.setLayoutCount = 1;
+  layout_info.pSetLayouts = &desc_set_layout;
+  layout_info.pushConstantRangeCount = 1;
+  layout_info.pPushConstantRanges = &range;
+
+  err = vkCreatePipelineLayout(_device, &layout_info, NULL, &_pipeline_layout);
+  if (err) {
+    vulkan_error(err, "Failed to create pipeline layout");
+    return;
+  }
+
+  _is_valid = true;
   _needs_reset = false;
 }
 
@@ -89,6 +154,251 @@ VulkanGraphicsStateGuardian::
 ~VulkanGraphicsStateGuardian() {
 }
 
+/**
+ * Creates whatever structures the GSG requires to represent the texture
+ * internally, and returns a newly-allocated TextureContext object with this
+ * data.  It is the responsibility of the calling function to later call
+ * release_texture() with this same pointer (which will also delete the
+ * pointer).
+ *
+ * This function should not be called directly to prepare a texture.  Instead,
+ * call Texture::prepare().
+ */
+TextureContext *VulkanGraphicsStateGuardian::
+prepare_texture(Texture *, int view) {
+  return (TextureContext *)NULL;
+}
+
+/**
+ * Ensures that the current Texture data is refreshed onto the GSG.  This
+ * means updating the texture properties and/or re-uploading the texture
+ * image, if necessary.  This should only be called within the draw thread.
+ *
+ * If force is true, this function will not return until the texture has been
+ * fully uploaded.  If force is false, the function may choose to upload a
+ * simple version of the texture instead, if the texture is not fully resident
+ * (and if get_incomplete_render() is true).
+ */
+bool VulkanGraphicsStateGuardian::
+update_texture(TextureContext *, bool force) {
+  return true;
+}
+
+/**
+ * Frees the resources previously allocated via a call to prepare_texture(),
+ * including deleting the TextureContext itself, if it is non-NULL.
+ */
+void VulkanGraphicsStateGuardian::
+release_texture(TextureContext *) {
+}
+
+/**
+ * This method should only be called by the GraphicsEngine.  Do not call it
+ * directly; call GraphicsEngine::extract_texture_data() instead.
+ *
+ * This method will be called in the draw thread to download the texture
+ * memory's image into its ram_image value.  It returns true on success, false
+ * otherwise.
+ */
+bool VulkanGraphicsStateGuardian::
+extract_texture_data(Texture *) {
+  return false;
+}
+
+/**
+ * Creates whatever structures the GSG requires to represent the sampler
+ * internally, and returns a newly-allocated SamplerContext object with this
+ * data.  It is the responsibility of the calling function to later call
+ * release_sampler() with this same pointer (which will also delete the
+ * pointer).
+ *
+ * This function should not be called directly to prepare a sampler.  Instead,
+ * call Texture::prepare().
+ */
+SamplerContext *VulkanGraphicsStateGuardian::
+prepare_sampler(const SamplerState &sampler) {
+  return (SamplerContext *)NULL;
+}
+
+/**
+ * Frees the resources previously allocated via a call to prepare_sampler(),
+ * including deleting the SamplerContext itself, if it is non-NULL.
+ */
+void VulkanGraphicsStateGuardian::
+release_sampler(SamplerContext *) {
+}
+
+/**
+ * Prepares the indicated Geom for retained-mode rendering, by creating
+ * whatever structures are necessary in the GSG (for instance, vertex
+ * buffers). Returns the newly-allocated GeomContext that can be used to
+ * render the geom.
+ */
+GeomContext *VulkanGraphicsStateGuardian::
+prepare_geom(Geom *) {
+  return (GeomContext *)NULL;
+}
+
+/**
+ * Frees the resources previously allocated via a call to prepare_geom(),
+ * including deleting the GeomContext itself, if it is non-NULL.
+ *
+ * This function should not be called directly to prepare a Geom.  Instead,
+ * call Geom::prepare().
+ */
+void VulkanGraphicsStateGuardian::
+release_geom(GeomContext *) {
+}
+
+/**
+ * Compile a vertex/fragment shader body.
+ */
+ShaderContext *VulkanGraphicsStateGuardian::
+prepare_shader(Shader *shader) {
+  if (shader->get_language() != Shader::SL_SPIR_V) {
+    vulkandisplay_cat.error()
+      << "Vulkan can only consume SPIR-V shaders.\n";
+    return (ShaderContext *)NULL;
+  }
+
+  VkResult err;
+  const Shader::ShaderType shader_types[] = {Shader::ST_vertex, Shader::ST_fragment};
+  VulkanShaderContext *sc = new VulkanShaderContext(shader);
+
+  for (int i = 0; i < 2; ++i) {
+    string code = shader->get_text(shader_types[i]);
+
+    VkShaderModuleCreateInfo module_info;
+    module_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+    module_info.pNext = NULL;
+    module_info.flags = 0;
+    module_info.codeSize = code.size();
+    module_info.pCode = (const uint32_t *)code.data();
+
+    err = vkCreateShaderModule(_device, &module_info, NULL, &sc->_modules[i]);
+    if (err) {
+      vulkan_error(err, "Failed to load shader module");
+      delete sc;
+      return (ShaderContext *)NULL;
+    }
+  }
+
+  return sc;
+}
+
+/**
+ * Releases the resources allocated by prepare_shader
+ */
+void VulkanGraphicsStateGuardian::
+release_shader(ShaderContext *sc) {
+}
+
+/**
+ * Prepares the indicated buffer for retained-mode rendering.
+ */
+VertexBufferContext *VulkanGraphicsStateGuardian::
+prepare_vertex_buffer(GeomVertexArrayData *array_data) {
+  VulkanGraphicsPipe *vkpipe;
+  DCAST_INTO_R(vkpipe, get_pipe(), NULL);
+
+  CPT(GeomVertexArrayDataHandle) handle = array_data->get_handle();
+  size_t data_size = handle->get_data_size_bytes();
+
+  VkBufferCreateInfo buf_info;
+  buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+  buf_info.pNext = NULL;
+  buf_info.flags = 0;
+  buf_info.size = data_size;
+  buf_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+  buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+  buf_info.queueFamilyIndexCount = 0;
+  buf_info.pQueueFamilyIndices = NULL;
+
+  VkResult err;
+  VkBuffer buffer;
+  err = vkCreateBuffer(_device, &buf_info, NULL, &buffer);
+  if (err)  {
+    vulkan_error(err, "Failed to create vertex buffer");
+    return NULL;
+  }
+
+  VkMemoryRequirements mem_reqs;
+  vkGetBufferMemoryRequirements(_device, buffer, &mem_reqs);
+
+  VkMemoryAllocateInfo alloc_info;
+  alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+  alloc_info.pNext = NULL;
+  alloc_info.allocationSize = mem_reqs.size;
+
+  // Find a host visible memory heap, since we're about to map it.
+  if (!vkpipe->find_memory_type(alloc_info.memoryTypeIndex, mem_reqs.memoryTypeBits,
+                                VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
+    vulkan_error(err, "Failed to find memory heap to allocate vertex buffer");
+    vkDestroyBuffer(_device, buffer, NULL);
+    return NULL;
+  }
+
+  VkDeviceMemory memory;
+  err = vkAllocateMemory(_device, &alloc_info, NULL, &memory);
+  if (err) {
+    vulkan_error(err, "Failed to allocate memory for vertex buffer");
+    vkDestroyBuffer(_device, buffer, NULL);
+    return NULL;
+  }
+
+  err = vkBindBufferMemory(_device, buffer, memory, 0);
+  if (err) {
+    vulkan_error(err, "Failed to bind memory to vertex buffer");
+    vkDestroyBuffer(_device, buffer, NULL);
+    vkFreeMemory(_device, memory, NULL);
+    return NULL;
+  }
+
+  VulkanVertexBufferContext *vbc = new VulkanVertexBufferContext(_prepared_objects, array_data);
+  vbc->_buffer = buffer;
+  vbc->_memory = memory;
+  vbc->update_data_size_bytes(alloc_info.allocationSize);
+
+  void *data;
+  err = vkMapMemory(_device, memory, 0, alloc_info.allocationSize, 0, &data);
+  if (err || !data) {
+    vulkan_error(err, "Failed to map vertex buffer memory");
+    vkDestroyBuffer(_device, buffer, NULL);
+    vkFreeMemory(_device, memory, NULL);
+    return NULL;
+  }
+
+  const unsigned char *source_data = handle->get_read_pointer(true);
+  memcpy(data, source_data, data_size);
+
+  vkUnmapMemory(_device, memory);
+  return vbc;
+}
+
+/**
+ * Frees the resources previously allocated via a call to prepare_data(),
+ * including deleting the VertexBufferContext itself, if necessary.
+ */
+void VulkanGraphicsStateGuardian::
+release_vertex_buffer(VertexBufferContext *) {
+}
+
+/**
+ * Prepares the indicated buffer for retained-mode rendering.
+ */
+IndexBufferContext *VulkanGraphicsStateGuardian::
+prepare_index_buffer(GeomPrimitive *) {
+  return (IndexBufferContext *)NULL;
+}
+
+/**
+ * Frees the resources previously allocated via a call to prepare_data(),
+ * including deleting the IndexBufferContext itself, if necessary.
+ */
+void VulkanGraphicsStateGuardian::
+release_index_buffer(IndexBufferContext *) {
+}
+
 /**
  * Creates a new GeomMunger object to munge vertices appropriate to this GSG
  * for the indicated state.
@@ -183,6 +493,19 @@ prepare_display_region(DisplayRegionPipelineReader *dr) {
   vkCmdSetScissor(_cmd, 0, count, &_viewports[0]);
 }
 
+/**
+ * Makes the current lens (whichever lens was most recently specified with
+ * set_scene()) active, so that it will transform future rendered geometry.
+ * Normally this is only called from the draw process, and usually it is
+ * called by set_scene().
+ *
+ * The return value is true if the lens is acceptable, false if it is not.
+ */
+bool VulkanGraphicsStateGuardian::
+prepare_lens() {
+  return true;
+}
+
 /**
  * Called before each frame is rendered, to allow the GSG a chance to do any
  * internal cleanup before beginning the frame.
@@ -206,9 +529,11 @@ begin_frame(Thread *current_thread) {
   alloc_info.commandBufferCount = 1;
 
   VkResult err;
-  err = vkAllocateCommandBuffers(_device, &alloc_info, &_cmd);
-  nassertr(!err, false);
-  nassertr(_cmd != VK_NULL_HANDLE, false);
+  if (_cmd == VK_NULL_HANDLE) {
+    err = vkAllocateCommandBuffers(_device, &alloc_info, &_cmd);
+    nassertr(!err, false);
+    nassertr(_cmd != VK_NULL_HANDLE, false);
+  }
 
   VkCommandBufferBeginInfo begin_info;
   begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
@@ -266,6 +591,336 @@ end_frame(Thread *current_thread) {
   //TODO: delete command buffer, schedule for deletion, or recycle.
 }
 
+/**
+ * Called before a sequence of draw_primitive() functions are called, this
+ * should prepare the vertex data for rendering.  It returns true if the
+ * vertices are ok, false to abort this group of primitives.
+ */
+bool VulkanGraphicsStateGuardian::
+begin_draw_primitives(const GeomPipelineReader *geom_reader,
+                      const GeomMunger *munger,
+                      const GeomVertexDataPipelineReader *data_reader,
+                      bool force) {
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader, force)) {
+    return false;
+  }
+
+  // Load the default shader.  Temporary hack.
+  static PT(Shader) default_shader;
+  if (default_shader.is_null()) {
+    default_shader = Shader::load(Shader::SL_SPIR_V, "tri-vert.spv", "tri-frag.spv");
+    nassertr(default_shader, NULL);
+
+    ShaderContext *sc = default_shader->prepare_now(get_prepared_objects(), this);
+    nassertr(sc, NULL);
+    _default_sc = DCAST(VulkanShaderContext, sc);
+  }
+
+  VkPipelineShaderStageCreateInfo stages[2];
+  memset(stages, 0, sizeof(stages));
+  stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+  stages[0].pNext = NULL;
+  stages[0].flags = 0;
+  stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
+  stages[0].module = _default_sc->_modules[0];
+  stages[0].pName = "main";
+  stages[0].pSpecializationInfo = NULL;
+
+  stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+  stages[1].pNext = NULL;
+  stages[1].flags = 0;
+  stages[1].stage = VK_SHADER_STAGE_VERTEX_BIT;
+  stages[1].module = _default_sc->_modules[1];
+  stages[1].pName = "main";
+  stages[1].pSpecializationInfo = NULL;
+
+  // Describe each vertex input binding (ie. GeomVertexArray).
+  const GeomVertexFormat *format = data_reader->get_format();
+  VkVertexInputBindingDescription *binding_desc = (VkVertexInputBindingDescription *)
+    alloca(sizeof(VkVertexInputBindingDescription) * format->get_num_arrays());
+
+  for (size_t i = 0; i < format->get_num_arrays(); ++i) {
+    binding_desc[i].binding = i;
+    binding_desc[i].stride = format->get_array(i)->get_stride();
+    binding_desc[i].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
+  }
+
+  // Now describe each vertex attribute (ie. GeomVertexColumn).
+  VkVertexInputAttributeDescription *attrib_desc = (VkVertexInputAttributeDescription *)
+    alloca(sizeof(VkVertexInputAttributeDescription) * format->get_num_columns());
+
+  for (size_t i = 0; i < format->get_num_columns(); ++i) {
+    const GeomVertexColumn *column = format->get_column(i);
+    attrib_desc[i].location = i;
+    attrib_desc[i].binding = format->get_array_with(i);
+    attrib_desc[i].offset = column->get_start();
+
+    // Determine which Vulkan format to map this column to.  The formats are
+    // laid out somewhat (though not entirely) consistently, so we can use a
+    // trick to jump to the format for the right number of components.
+    int fmt_jump = column->get_num_components() - 1;
+    switch (column->get_numeric_type()) {
+    case GeomEnums::NT_uint8:
+      if (fmt_jump < 3) {
+        attrib_desc[i].format = (VkFormat)(VK_FORMAT_R8_UINT + 7 * fmt_jump);
+      } else {
+        attrib_desc[i].format = VK_FORMAT_R8G8B8A8_UINT;
+      }
+      break;
+    case GeomEnums::NT_uint16:
+      attrib_desc[i].format = (VkFormat)(VK_FORMAT_R16_UINT + 7 * fmt_jump);
+      break;
+    case GeomEnums::NT_uint32:
+      attrib_desc[i].format = (VkFormat)(VK_FORMAT_R32_UINT + 3 * fmt_jump);
+      break;
+    case GeomEnums::NT_packed_dcba:
+      attrib_desc[i].format = VK_FORMAT_B8G8R8A8_UINT;
+      break;
+    case GeomEnums::NT_packed_dabc:
+      attrib_desc[i].format = VK_FORMAT_A8B8G8R8_UINT_PACK32;
+      break;
+    case GeomEnums::NT_float32:
+      attrib_desc[i].format = (VkFormat)(VK_FORMAT_R32_SFLOAT + 3 * fmt_jump);
+      break;
+    case GeomEnums::NT_float64:
+      attrib_desc[i].format = (VkFormat)(VK_FORMAT_R64_SFLOAT + 3 * fmt_jump);
+      break;
+    case GeomEnums::NT_int8:
+      if (fmt_jump < 3) {
+        attrib_desc[i].format = (VkFormat)(VK_FORMAT_R8_SINT + 7 * fmt_jump);
+      } else {
+        attrib_desc[i].format = VK_FORMAT_R8G8B8A8_SINT;
+      }
+      break;
+    case GeomEnums::NT_int16:
+      attrib_desc[i].format = (VkFormat)(VK_FORMAT_R16_SINT + 7 * fmt_jump);
+      break;
+    case GeomEnums::NT_int32:
+      attrib_desc[i].format = (VkFormat)(VK_FORMAT_R32_SINT + 3 * fmt_jump);
+      break;
+    case GeomEnums::NT_packed_ufloat:
+      attrib_desc[i].format = VK_FORMAT_B10G11R11_UFLOAT_PACK32;
+      break;
+    }
+  }
+
+  VkPipelineVertexInputStateCreateInfo vertex_info;
+  memset(&vertex_info, 0, sizeof(vertex_info));
+  vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+  vertex_info.pNext = NULL;
+  vertex_info.flags = 0;
+  vertex_info.vertexBindingDescriptionCount = format->get_num_arrays();
+  vertex_info.pVertexBindingDescriptions = binding_desc;
+  vertex_info.vertexAttributeDescriptionCount = 2;//format->get_num_columns();
+  vertex_info.pVertexAttributeDescriptions = attrib_desc;
+
+  VkPipelineInputAssemblyStateCreateInfo assembly_info;
+  memset(&assembly_info, 0, sizeof(assembly_info));
+  assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+  assembly_info.pNext = NULL;
+  assembly_info.flags = 0;
+  assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+  assembly_info.primitiveRestartEnable = VK_FALSE;
+
+  VkPipelineViewportStateCreateInfo viewport_info;
+  memset(&viewport_info, 0, sizeof(viewport_info));
+  viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+  viewport_info.pNext = NULL;
+  viewport_info.flags = 0;
+  viewport_info.viewportCount = 1;
+  viewport_info.pViewports = NULL;
+  viewport_info.scissorCount = 1;
+  viewport_info.pScissors = NULL;
+
+  VkPipelineRasterizationStateCreateInfo raster_info;
+  memset(&raster_info, 0, sizeof(raster_info));
+  raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+  raster_info.pNext = NULL;
+  raster_info.flags = 0;
+  raster_info.depthClampEnable = VK_TRUE;
+  raster_info.rasterizerDiscardEnable = VK_FALSE;
+  raster_info.polygonMode = VK_POLYGON_MODE_FILL;
+  raster_info.cullMode = VK_CULL_MODE_BACK_BIT;
+  raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+  raster_info.depthBiasEnable = VK_FALSE;
+  raster_info.depthBiasConstantFactor = 0;
+  raster_info.depthBiasClamp = 0;
+  raster_info.depthBiasSlopeFactor = 0;
+  raster_info.lineWidth = 0;
+
+  VkPipelineMultisampleStateCreateInfo ms_info;
+  memset(&ms_info, 0, sizeof(ms_info));
+  ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+  ms_info.pNext = NULL;
+  ms_info.flags = 0;
+  ms_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+  ms_info.sampleShadingEnable = VK_FALSE;
+  ms_info.minSampleShading = 0.0;
+  ms_info.pSampleMask = NULL;
+  ms_info.alphaToCoverageEnable = VK_FALSE;
+  ms_info.alphaToOneEnable = VK_FALSE;
+
+  VkPipelineDepthStencilStateCreateInfo ds_info;
+  memset(&ds_info, 0, sizeof(ds_info));
+  ds_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+  ds_info.pNext = NULL;
+  ds_info.flags = 0;
+  ds_info.depthTestEnable = VK_TRUE;
+  ds_info.depthWriteEnable = VK_TRUE;
+  ds_info.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
+  ds_info.depthBoundsTestEnable = VK_FALSE;
+  ds_info.stencilTestEnable = VK_FALSE;
+  ds_info.front.failOp = VK_STENCIL_OP_KEEP;
+  ds_info.front.passOp = VK_STENCIL_OP_KEEP;
+  ds_info.front.depthFailOp = VK_STENCIL_OP_KEEP;
+  ds_info.front.compareOp = VK_COMPARE_OP_ALWAYS;
+  ds_info.front.compareMask = 0;
+  ds_info.front.writeMask = 0;
+  ds_info.front.reference = 0;
+  ds_info.back = ds_info.front;
+  ds_info.minDepthBounds = 0;
+  ds_info.maxDepthBounds = 0;
+
+  VkPipelineColorBlendAttachmentState att_state[1];
+  att_state[0].blendEnable = VK_FALSE;
+  att_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
+  att_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
+  att_state[0].colorBlendOp = VK_BLEND_OP_ADD;
+  att_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
+  att_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+  att_state[0].alphaBlendOp = VK_BLEND_OP_ADD;
+  att_state[0].colorWriteMask = 0xf;
+
+  VkPipelineColorBlendStateCreateInfo blend_info;
+  memset(&blend_info, 0, sizeof(blend_info));
+  blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+  blend_info.pNext = NULL;
+  blend_info.flags = 0;
+  blend_info.logicOpEnable = VK_FALSE;
+  blend_info.logicOp = VK_LOGIC_OP_NO_OP;
+  blend_info.attachmentCount = 1;
+  blend_info.pAttachments = att_state;
+  blend_info.blendConstants[0] = 1.0f;
+  blend_info.blendConstants[1] = 1.0f;
+  blend_info.blendConstants[2] = 1.0f;
+  blend_info.blendConstants[3] = 1.0f;
+
+  // Tell Vulkan that we'll be specifying the viewport and scissor separately.
+  const VkDynamicState dynamic_states[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
+
+  VkPipelineDynamicStateCreateInfo dynamic_info;
+  memset(&dynamic_info, 0, sizeof(dynamic_info));
+  dynamic_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+  dynamic_info.pNext = NULL;
+  dynamic_info.flags = 0;
+  dynamic_info.dynamicStateCount = 2;
+  dynamic_info.pDynamicStates = dynamic_states;
+
+  VkGraphicsPipelineCreateInfo pipeline_info;
+  memset(&pipeline_info, 0, sizeof(pipeline_info));
+  pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+  pipeline_info.pNext = NULL;
+  pipeline_info.flags = 0;
+  pipeline_info.stageCount = 2;
+  pipeline_info.pStages = stages;
+  pipeline_info.pVertexInputState = &vertex_info;
+  pipeline_info.pInputAssemblyState = &assembly_info;
+  pipeline_info.pTessellationState = NULL;
+  pipeline_info.pViewportState = &viewport_info;
+  pipeline_info.pRasterizationState = &raster_info;
+  pipeline_info.pMultisampleState = &ms_info;
+  pipeline_info.pDepthStencilState = &ds_info;
+  pipeline_info.pColorBlendState = &blend_info;
+  pipeline_info.pDynamicState = &dynamic_info;
+  pipeline_info.layout = _pipeline_layout;
+  pipeline_info.renderPass = _render_pass;
+  pipeline_info.subpass = 0;
+  pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
+  pipeline_info.basePipelineIndex = 0;
+
+  VkResult err;
+  err = vkCreateGraphicsPipelines(_device, 0, 1, &pipeline_info, NULL, &_pipeline);
+  if (err) {
+    vulkan_error(err, "Failed to create graphics pipeline");
+    return false;
+  }
+
+  vkCmdBindPipeline(_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipeline);
+
+  return true;
+}
+
+/**
+ * Draws a series of disconnected triangles.
+ */
+bool VulkanGraphicsStateGuardian::
+draw_triangles(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
+/**
+ * Draws a series of triangle strips.
+ */
+bool VulkanGraphicsStateGuardian::
+draw_tristrips(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
+/**
+ * Draws a series of triangle fans.
+ */
+bool VulkanGraphicsStateGuardian::
+draw_trifans(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
+/**
+ * Draws a series of "patches", which can only be processed by a tessellation
+ * shader.
+ */
+bool VulkanGraphicsStateGuardian::
+draw_patches(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
+/**
+ * Draws a series of disconnected line segments.
+ */
+bool VulkanGraphicsStateGuardian::
+draw_lines(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
+/**
+ * Draws a series of line strips.
+ */
+bool VulkanGraphicsStateGuardian::
+draw_linestrips(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
+/**
+ * Draws a series of disconnected points.
+ */
+bool VulkanGraphicsStateGuardian::
+draw_points(const GeomPrimitivePipelineReader *, bool) {
+  return false;
+}
+
+/**
+ * Called after a sequence of draw_primitive() functions are called, this
+ * should do whatever cleanup is appropriate.
+ */
+void VulkanGraphicsStateGuardian::
+end_draw_primitives() {
+  if (_pipeline != VK_NULL_HANDLE) {
+    vkDestroyPipeline(_device, _pipeline, NULL);
+    _pipeline = VK_NULL_HANDLE;
+  }
+
+  GraphicsStateGuardian::end_draw_primitives();
+}
+
 /**
  * Resets all internal state as if the gsg were newly created.
  */

+ 50 - 3
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.h

@@ -16,29 +16,71 @@
 
 #include "config_vulkandisplay.h"
 
-#include <vulkan/vulkan.h>
+class VulkanShaderContext;
 
 /**
  * Manages a Vulkan device, and manages sending render commands to this
  * device.
  */
-class VulkanGraphicsStateGuardian : public GraphicsStateGuardian {
+class VulkanGraphicsStateGuardian FINAL : public GraphicsStateGuardian {
 public:
   VulkanGraphicsStateGuardian(GraphicsEngine *engine, VulkanGraphicsPipe *pipe,
-                              VulkanGraphicsStateGuardian *share_with);
+                              VulkanGraphicsStateGuardian *share_with,
+                              uint32_t queue_family_index);
   virtual ~VulkanGraphicsStateGuardian();
 
+  virtual TextureContext *prepare_texture(Texture *tex, int view);
+  virtual bool update_texture(TextureContext *tc, bool force);
+  virtual void release_texture(TextureContext *tc);
+  virtual bool extract_texture_data(Texture *tex);
+
+  virtual SamplerContext *prepare_sampler(const SamplerState &sampler);
+  virtual void release_sampler(SamplerContext *sc);
+
+  virtual GeomContext *prepare_geom(Geom *geom);
+  virtual void release_geom(GeomContext *gc);
+
+  virtual ShaderContext *prepare_shader(Shader *shader);
+  virtual void release_shader(ShaderContext *sc);
+
+  virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
+  virtual void release_vertex_buffer(VertexBufferContext *vbc);
+
+  virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
+  virtual void release_index_buffer(IndexBufferContext *ibc);
+
   virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
                                           Thread *current_thread);
 
   virtual void clear(DrawableRegion *clearable);
   virtual void prepare_display_region(DisplayRegionPipelineReader *dr);
+  virtual bool prepare_lens();
 
   virtual bool begin_frame(Thread *current_thread);
   virtual bool begin_scene();
   virtual void end_scene();
   virtual void end_frame(Thread *current_thread);
 
+  virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader,
+                                     const GeomMunger *munger,
+                                     const GeomVertexDataPipelineReader *data_reader,
+                                     bool force);
+  virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader,
+                              bool force);
+  virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader,
+                              bool force);
+  virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader,
+                            bool force);
+  virtual bool draw_patches(const GeomPrimitivePipelineReader *reader,
+                            bool force);
+  virtual bool draw_lines(const GeomPrimitivePipelineReader *reader,
+                          bool force);
+  virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader,
+                               bool force);
+  virtual bool draw_points(const GeomPrimitivePipelineReader *reader,
+                           bool force);
+  virtual void end_draw_primitives();
+
   virtual void reset();
 
 private:
@@ -47,6 +89,11 @@ private:
   VkCommandPool _cmd_pool;
   VkCommandBuffer _cmd;
   pvector<VkRect2D> _viewports;
+  VkRenderPass _render_pass;
+  VkPipelineCache _pipeline_cache;
+  VkPipelineLayout _pipeline_layout;
+  VkPipeline _pipeline;
+  VulkanShaderContext *_default_sc;
 
   friend class VulkanGraphicsWindow;
 

+ 51 - 8
panda/src/vulkandisplay/vulkanGraphicsWindow.cxx

@@ -79,8 +79,8 @@ begin_frame(FrameMode mode, Thread *current_thread) {
 
   // Instruct the GSG that we are commencing a new frame.  This will cause it
   // to create a command buffer.
-  _gsg->set_current_properties(&get_fb_properties());
-  if (!_gsg->begin_frame(current_thread)) {
+  vkgsg->set_current_properties(&get_fb_properties());
+  if (!vkgsg->begin_frame(current_thread)) {
     return false;
   }
 
@@ -126,6 +126,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   begin_info.pClearValues = clears;
 
   vkCmdBeginRenderPass(cmd, &begin_info, VK_SUBPASS_CONTENTS_INLINE);
+  vkgsg->_render_pass = _render_pass;
 
   return true;
 }
@@ -146,6 +147,7 @@ end_frame(FrameMode mode, Thread *current_thread) {
     nassertv(cmd != VK_NULL_HANDLE);
 
     vkCmdEndRenderPass(cmd);
+    vkgsg->_render_pass = VK_NULL_HANDLE;
 
     if (mode == FM_render) {
       copy_to_textures();
@@ -294,13 +296,46 @@ open_window() {
   // Make sure we have a GSG, which manages a VkDevice.
   VulkanGraphicsStateGuardian *vkgsg;
   if (_gsg == NULL) {
+    // Find a queue suitable both for graphics and for presenting to our
+    // surface.  TODO: fall back to separate graphics/present queues?
+    uint32_t queue_family_index;
+    if (!vkpipe->find_queue_family_for_surface(queue_family_index, _surface, VK_QUEUE_GRAPHICS_BIT)) {
+      vulkan_error(err, "Failed to find graphics queue that can present to surface");
+      return false;
+    }
+
     // There is no old gsg.  Create a new one.
-    vkgsg = new VulkanGraphicsStateGuardian(_engine, vkpipe, NULL);
+    vkgsg = new VulkanGraphicsStateGuardian(_engine, vkpipe, NULL, queue_family_index);
     _gsg = vkgsg;
+  } else {
+    //TODO: check that the GSG's queue can present to our surface.
   }
 
   VkDevice device = vkgsg->_device;
 
+  // Query the preferred image formats for this surface.
+  uint32_t num_formats;
+  err = vkGetPhysicalDeviceSurfaceFormatsKHR(vkpipe->_gpu, _surface,
+                                             &num_formats, NULL);
+  nassertr(!err, false);
+  VkSurfaceFormatKHR *formats =
+      (VkSurfaceFormatKHR *)alloca(sizeof(VkSurfaceFormatKHR) * num_formats);
+  err = vkGetPhysicalDeviceSurfaceFormatsKHR(vkpipe->_gpu, _surface,
+                                             &num_formats, formats);
+  nassertr(!err, false);
+
+  // Get the surface capabilities to make sure we make a compatible swapchain.
+  VkSurfaceCapabilitiesKHR surf_caps;
+  err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkpipe->_gpu, _surface, &surf_caps);
+  if (err) {
+    vulkan_error(err, "Failed to get surface capabilities");
+    return false;
+  }
+
+  uint32_t num_images = (uint32_t)(1 + _fb_properties.get_back_buffers());
+  num_images = min(surf_caps.maxImageCount, num_images);
+  num_images = max(surf_caps.minImageCount, num_images);
+
   // Get the supported presentation modes for this surface.
   uint32_t num_present_modes = 0;
   err = vkGetPhysicalDeviceSurfacePresentModesKHR(vkpipe->_gpu, _surface, &num_present_modes, NULL);
@@ -312,9 +347,7 @@ open_window() {
   swapchain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
   swapchain_info.pNext = NULL;
   swapchain_info.surface = _surface;
-  swapchain_info.minImageCount = 1 + _fb_properties.get_back_buffers();
-  swapchain_info.imageFormat = VK_FORMAT_B8G8R8A8_UNORM;
-  swapchain_info.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
+  swapchain_info.minImageCount = num_images;
   swapchain_info.imageExtent.width = _size[0];
   swapchain_info.imageExtent.height = _size[1];
   swapchain_info.imageArrayLayers = 1;
@@ -324,12 +357,23 @@ open_window() {
   swapchain_info.pQueueFamilyIndices = NULL;
   swapchain_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
   swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
-  swapchain_info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
   swapchain_info.clipped = true;
   swapchain_info.oldSwapchain = NULL;
 
+  // If the format list includes just one entry of VK_FORMAT_UNDEFINED, the
+  // surface has no preferred format.  Otherwise, at least one supported
+  // format will be returned.
+  if (num_formats == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
+    swapchain_info.imageFormat = VK_FORMAT_B8G8R8A8_UNORM;
+  } else {
+    nassertr(num_formats > 1, false);
+    swapchain_info.imageFormat = formats[0].format;
+  }
+  swapchain_info.imageColorSpace = formats[0].colorSpace;
+
   // Choose a present mode.  Use FIFO mode as fallback, which is always
   // available.
+  swapchain_info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
   for (size_t i = 0; i < num_present_modes; ++i) {
     if (present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
       // This is the lowest-latency non-tearing mode, so we'll take this.
@@ -350,7 +394,6 @@ open_window() {
   }
 
   // Get the images in the swap chain, which may be more than requested.
-  uint32_t num_images;
   vkGetSwapchainImagesKHR(device, _swapchain, &num_images, NULL);
   _image_views.resize(num_images);
   _framebuffers.resize(num_images);

+ 0 - 3
panda/src/vulkandisplay/vulkanGraphicsWindow.h

@@ -15,9 +15,6 @@
 #define VULKANGRAPHICSWINDOW_H
 
 #include "config_vulkandisplay.h"
-#include "winGraphicsWindow.h"
-
-#include <vulkan/vulkan.h>
 
 #ifdef _WIN32
 #include "winGraphicsWindow.h"

+ 23 - 0
panda/src/vulkandisplay/vulkanShaderContext.I

@@ -0,0 +1,23 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file vulkanShaderContext.I
+ * @author rdb
+ * @date 2016-02-18
+ */
+
+
+/**
+ * Constructs a shader context.  Follow this up with calls to fill in the
+ * module array.
+ */
+INLINE VulkanShaderContext::
+VulkanShaderContext(Shader *shader) : ShaderContext(shader) {
+  _modules[0] = VK_NULL_HANDLE;
+  _modules[1] = VK_NULL_HANDLE;
+}

+ 16 - 0
panda/src/vulkandisplay/vulkanShaderContext.cxx

@@ -0,0 +1,16 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file vulkanShaderContext.cxx
+ * @author rdb
+ * @date 2016-02-18
+ */
+
+#include "vulkanShaderContext.h"
+
+TypeHandle VulkanShaderContext::_type_handle;

+ 52 - 0
panda/src/vulkandisplay/vulkanShaderContext.h

@@ -0,0 +1,52 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file vulkanShaderContext.h
+ * @author rdb
+ * @date 2016-02-18
+ */
+
+#ifndef VULKANSHADERCONTEXT_H
+#define VULKANSHADERCONTEXT_H
+
+#include "config_vulkandisplay.h"
+
+/**
+ * Manages a set of Vulkan shader modules.
+ */
+class EXPCL_VULKANDISPLAY VulkanShaderContext : public ShaderContext {
+public:
+  INLINE VulkanShaderContext(Shader *shader);
+  ~VulkanShaderContext() {};
+
+  ALLOC_DELETED_CHAIN(VulkanShaderContext);
+
+public:
+  VkShaderModule _modules[2];
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ShaderContext::init_type();
+    register_type(_type_handle, "VulkanShaderContext",
+                  ShaderContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "vulkanShaderContext.I"
+
+#endif  // VULKANSHADERCONTEXT_H

+ 23 - 0
panda/src/vulkandisplay/vulkanVertexBufferContext.I

@@ -0,0 +1,23 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file vulkanVertexBufferContext.I
+ * @author rdb
+ * @date 2016-02-18
+ */
+
+/**
+ *
+ */
+INLINE VulkanVertexBufferContext::
+VulkanVertexBufferContext(PreparedGraphicsObjects *pgo,
+                          GeomVertexArrayData *data) :
+  VertexBufferContext(pgo, data),
+  _buffer(VK_NULL_HANDLE),
+  _memory(VK_NULL_HANDLE) {
+}

+ 34 - 0
panda/src/vulkandisplay/vulkanVertexBufferContext.cxx

@@ -0,0 +1,34 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file vulkanVertexBufferContext.cxx
+ * @author rdb
+ * @date 2016-02-18
+ */
+
+TypeHandle VulkanVertexBufferContext::_type_handle;
+
+/**
+ * Evicts the page from the LRU.  Called internally when the LRU determines
+ * that it is full.  May also be called externally when necessary to
+ * explicitly evict the page.
+ *
+ * It is legal for this method to either evict the page as requested, do
+ * nothing (in which case the eviction will be requested again at the next
+ * epoch), or requeue itself on the tail of the queue (in which case the
+ * eviction will be requested again much later).
+ */
+void VulkanVertexBufferContext::
+evict_lru() {
+  /*dequeue_lru();
+
+  TODO: manage freeing when not currently bound
+
+  update_data_size_bytes(0);
+  mark_unloaded();*/
+}

+ 56 - 0
panda/src/vulkandisplay/vulkanVertexBufferContext.h

@@ -0,0 +1,56 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file vulkanVertexBufferContext.h
+ * @author rdb
+ * @date 2016-02-18
+ */
+
+#ifndef VULKANVERTEXBUFFERCONTEXT_H
+#define VULKANVERTEXBUFFERCONTEXT_H
+
+#include "config_vulkandisplay.h"
+#include "vertexBufferContext.h"
+#include "deletedChain.h"
+
+/**
+ * Manages a buffer used for holding vertex data and its allocated GPU memory.
+ */
+class EXPCL_VULKANDISPLAY VulkanVertexBufferContext : public VertexBufferContext {
+public:
+  INLINE VulkanVertexBufferContext(PreparedGraphicsObjects *pgo,
+                                   GeomVertexArrayData *data);
+  ALLOC_DELETED_CHAIN(VulkanVertexBufferContext);
+
+  virtual void evict_lru();
+
+public:
+  VkBuffer _buffer;
+  VkDeviceMemory _memory;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    VertexBufferContext::init_type();
+    register_type(_type_handle, "VulkanVertexBufferContext",
+                  VertexBufferContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "vulkanVertexBufferContext.I"
+
+#endif