Bläddra i källkod

libobs: Add support for volume textures

Also fix some mip calculations along the way.
jpark37 5 år sedan
förälder
incheckning
8f6984e345

+ 1 - 0
libobs-d3d11/CMakeLists.txt

@@ -26,6 +26,7 @@ set(libobs-d3d11_SOURCES
 	d3d11-stagesurf.cpp
 	d3d11-stagesurf.cpp
 	d3d11-subsystem.cpp
 	d3d11-subsystem.cpp
 	d3d11-texture2d.cpp
 	d3d11-texture2d.cpp
+	d3d11-texture3d.cpp
 	d3d11-vertexbuffer.cpp
 	d3d11-vertexbuffer.cpp
 	d3d11-duplicator.cpp
 	d3d11-duplicator.cpp
 	d3d11-rebuild.cpp
 	d3d11-rebuild.cpp

+ 70 - 0
libobs-d3d11/d3d11-rebuild.cpp

@@ -251,6 +251,70 @@ void gs_timer_range::Rebuild(ID3D11Device *dev)
 		throw HRError("Failed to create timer", hr);
 		throw HRError("Failed to create timer", hr);
 }
 }
 
 
+void gs_texture_3d::RebuildSharedTextureFallback()
+{
+	td = {};
+	td.Width = 2;
+	td.Height = 2;
+	td.Depth = 2;
+	td.MipLevels = 1;
+	td.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
+	td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+
+	width = td.Width;
+	height = td.Height;
+	depth = td.Depth;
+	dxgiFormat = td.Format;
+	levels = 1;
+
+	resourceDesc = {};
+	resourceDesc.Format = td.Format;
+	resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+	resourceDesc.Texture3D.MostDetailedMip = 0;
+	resourceDesc.Texture3D.MipLevels = 1;
+
+	isShared = false;
+}
+
+void gs_texture_3d::Rebuild(ID3D11Device *dev)
+{
+	HRESULT hr;
+	if (isShared) {
+		hr = dev->OpenSharedResource((HANDLE)(uintptr_t)sharedHandle,
+					     __uuidof(ID3D11Texture3D),
+					     (void **)&texture);
+		if (FAILED(hr)) {
+			blog(LOG_WARNING,
+			     "Failed to rebuild shared texture: ", "0x%08lX",
+			     hr);
+			RebuildSharedTextureFallback();
+		}
+	}
+
+	if (!isShared) {
+		hr = dev->CreateTexture3D(
+			&td, data.size() ? srd.data() : nullptr, &texture);
+		if (FAILED(hr))
+			throw HRError("Failed to create 3D texture", hr);
+	}
+
+	hr = dev->CreateShaderResourceView(texture, &resourceDesc, &shaderRes);
+	if (FAILED(hr))
+		throw HRError("Failed to create resource view", hr);
+
+	if (isRenderTarget)
+		InitRenderTargets();
+
+	acquired = false;
+
+	if ((td.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) != 0) {
+		ComQIPtr<IDXGIResource> dxgi_res(texture);
+		if (dxgi_res)
+			GetSharedHandle(dxgi_res);
+		device_texture_acquire_sync(this, 0, INFINITE);
+	}
+}
+
 void SavedBlendState::Rebuild(ID3D11Device *dev)
 void SavedBlendState::Rebuild(ID3D11Device *dev)
 {
 {
 	HRESULT hr = dev->CreateBlendState(&bd, &state);
 	HRESULT hr = dev->CreateBlendState(&bd, &state);
@@ -327,6 +391,9 @@ try {
 		case gs_type::gs_timer_range:
 		case gs_type::gs_timer_range:
 			((gs_timer_range *)obj)->Release();
 			((gs_timer_range *)obj)->Release();
 			break;
 			break;
+		case gs_type::gs_texture_3d:
+			((gs_texture_3d *)obj)->Release();
+			break;
 		}
 		}
 
 
 		obj = obj->next;
 		obj = obj->next;
@@ -409,6 +476,9 @@ try {
 		case gs_type::gs_timer_range:
 		case gs_type::gs_timer_range:
 			((gs_timer_range *)obj)->Rebuild(dev);
 			((gs_timer_range *)obj)->Rebuild(dev);
 			break;
 			break;
+		case gs_type::gs_texture_3d:
+			((gs_texture_3d *)obj)->Rebuild(dev);
+			break;
 		}
 		}
 
 
 		obj = obj->next;
 		obj = obj->next;

+ 15 - 11
libobs-d3d11/d3d11-subsystem.cpp

@@ -1145,19 +1145,23 @@ gs_texture_t *device_cubetexture_create(gs_device_t *device, uint32_t size,
 gs_texture_t *device_voltexture_create(gs_device_t *device, uint32_t width,
 gs_texture_t *device_voltexture_create(gs_device_t *device, uint32_t width,
 				       uint32_t height, uint32_t depth,
 				       uint32_t height, uint32_t depth,
 				       enum gs_color_format color_format,
 				       enum gs_color_format color_format,
-				       uint32_t levels, const uint8_t **data,
+				       uint32_t levels,
+				       const uint8_t *const *data,
 				       uint32_t flags)
 				       uint32_t flags)
 {
 {
-	/* TODO */
-	UNUSED_PARAMETER(device);
-	UNUSED_PARAMETER(width);
-	UNUSED_PARAMETER(height);
-	UNUSED_PARAMETER(depth);
-	UNUSED_PARAMETER(color_format);
-	UNUSED_PARAMETER(levels);
-	UNUSED_PARAMETER(data);
-	UNUSED_PARAMETER(flags);
-	return NULL;
+	gs_texture *texture = NULL;
+	try {
+		texture = new gs_texture_3d(device, width, height, depth,
+					    color_format, levels, data, flags);
+	} catch (const HRError &error) {
+		blog(LOG_ERROR, "device_voltexture_create (D3D11): %s (%08lX)",
+		     error.str, error.hr);
+		LogD3D11ErrorDetails(error, device);
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_voltexture_create (D3D11): %s", error);
+	}
+
+	return texture;
 }
 }
 
 
 gs_zstencil_t *device_zstencil_create(gs_device_t *device, uint32_t width,
 gs_zstencil_t *device_zstencil_create(gs_device_t *device, uint32_t width,

+ 54 - 3
libobs-d3d11/d3d11-subsystem.hpp

@@ -294,6 +294,7 @@ enum class gs_type {
 	gs_swap_chain,
 	gs_swap_chain,
 	gs_timer,
 	gs_timer,
 	gs_timer_range,
 	gs_timer_range,
+	gs_texture_3d,
 };
 };
 
 
 struct gs_obj {
 struct gs_obj {
@@ -458,10 +459,10 @@ struct gs_texture_2d : gs_texture {
 	D3D11_TEXTURE2D_DESC td = {};
 	D3D11_TEXTURE2D_DESC td = {};
 
 
 	void InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd);
 	void InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd);
-	void InitTexture(const uint8_t **data);
+	void InitTexture(const uint8_t *const *data);
 	void InitResourceView();
 	void InitResourceView();
 	void InitRenderTargets();
 	void InitRenderTargets();
-	void BackupTexture(const uint8_t **data);
+	void BackupTexture(const uint8_t *const *data);
 	void GetSharedHandle(IDXGIResource *dxgi_res);
 	void GetSharedHandle(IDXGIResource *dxgi_res);
 
 
 	void RebuildSharedTextureFallback();
 	void RebuildSharedTextureFallback();
@@ -482,7 +483,7 @@ struct gs_texture_2d : gs_texture {
 
 
 	gs_texture_2d(gs_device_t *device, uint32_t width, uint32_t height,
 	gs_texture_2d(gs_device_t *device, uint32_t width, uint32_t height,
 		      gs_color_format colorFormat, uint32_t levels,
 		      gs_color_format colorFormat, uint32_t levels,
-		      const uint8_t **data, uint32_t flags,
+		      const uint8_t *const *data, uint32_t flags,
 		      gs_texture_type type, bool gdiCompatible,
 		      gs_texture_type type, bool gdiCompatible,
 		      bool nv12 = false);
 		      bool nv12 = false);
 
 
@@ -491,6 +492,56 @@ struct gs_texture_2d : gs_texture {
 	gs_texture_2d(gs_device_t *device, uint32_t handle);
 	gs_texture_2d(gs_device_t *device, uint32_t handle);
 };
 };
 
 
+struct gs_texture_3d : gs_texture {
+	ComPtr<ID3D11Texture3D> texture;
+	ComPtr<ID3D11RenderTargetView> renderTarget[6];
+
+	uint32_t width = 0, height = 0, depth = 0;
+	uint32_t flags = 0;
+	DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN;
+	bool isRenderTarget = false;
+	bool isDynamic = false;
+	bool isShared = false;
+	bool genMipmaps = false;
+	uint32_t sharedHandle = GS_INVALID_HANDLE;
+
+	bool chroma = false;
+	bool acquired = false;
+
+	vector<vector<uint8_t>> data;
+	vector<D3D11_SUBRESOURCE_DATA> srd;
+	D3D11_TEXTURE3D_DESC td = {};
+
+	void InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd);
+	void InitTexture(const uint8_t *const *data);
+	void InitResourceView();
+	void InitRenderTargets();
+	void BackupTexture(const uint8_t *const *data);
+	void GetSharedHandle(IDXGIResource *dxgi_res);
+
+	void RebuildSharedTextureFallback();
+	void Rebuild(ID3D11Device *dev);
+	void RebuildNV12_Y(ID3D11Device *dev);
+	void RebuildNV12_UV(ID3D11Device *dev);
+
+	inline void Release()
+	{
+		texture.Release();
+		for (auto &rt : renderTarget)
+			rt.Release();
+		shaderRes.Release();
+	}
+
+	inline gs_texture_3d() : gs_texture(GS_TEXTURE_3D, 0, GS_UNKNOWN) {}
+
+	gs_texture_3d(gs_device_t *device, uint32_t width, uint32_t height,
+		      uint32_t depth, gs_color_format colorFormat,
+		      uint32_t levels, const uint8_t *const *data,
+		      uint32_t flags);
+
+	gs_texture_3d(gs_device_t *device, uint32_t handle);
+};
+
 struct gs_zstencil_buffer : gs_obj {
 struct gs_zstencil_buffer : gs_obj {
 	ComPtr<ID3D11Texture2D> texture;
 	ComPtr<ID3D11Texture2D> texture;
 	ComPtr<ID3D11DepthStencilView> view;
 	ComPtr<ID3D11DepthStencilView> view;

+ 8 - 6
libobs-d3d11/d3d11-texture2d.cpp

@@ -27,7 +27,7 @@ void gs_texture_2d::InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd)
 	size_t curTex = 0;
 	size_t curTex = 0;
 
 
 	if (!actual_levels)
 	if (!actual_levels)
-		actual_levels = gs_get_total_levels(width, height);
+		actual_levels = gs_get_total_levels(width, height, 1);
 
 
 	rowSizeBytes /= 8;
 	rowSizeBytes /= 8;
 
 
@@ -48,7 +48,7 @@ void gs_texture_2d::InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd)
 	}
 	}
 }
 }
 
 
-void gs_texture_2d::BackupTexture(const uint8_t **data)
+void gs_texture_2d::BackupTexture(const uint8_t *const *data)
 {
 {
 	this->data.resize(levels);
 	this->data.resize(levels);
 
 
@@ -66,8 +66,10 @@ void gs_texture_2d::BackupTexture(const uint8_t **data)
 		vector<uint8_t> &subData = this->data[i];
 		vector<uint8_t> &subData = this->data[i];
 		memcpy(&subData[0], data[i], texSize);
 		memcpy(&subData[0], data[i], texSize);
 
 
-		w /= 2;
-		h /= 2;
+		if (w > 1)
+			w /= 2;
+		if (h > 1)
+			h /= 2;
 	}
 	}
 }
 }
 
 
@@ -87,7 +89,7 @@ void gs_texture_2d::GetSharedHandle(IDXGIResource *dxgi_res)
 	}
 	}
 }
 }
 
 
-void gs_texture_2d::InitTexture(const uint8_t **data)
+void gs_texture_2d::InitTexture(const uint8_t *const *data)
 {
 {
 	HRESULT hr;
 	HRESULT hr;
 
 
@@ -226,7 +228,7 @@ void gs_texture_2d::InitRenderTargets()
 
 
 gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width,
 gs_texture_2d::gs_texture_2d(gs_device_t *device, uint32_t width,
 			     uint32_t height, gs_color_format colorFormat,
 			     uint32_t height, gs_color_format colorFormat,
-			     uint32_t levels, const uint8_t **data,
+			     uint32_t levels, const uint8_t *const *data,
 			     uint32_t flags_, gs_texture_type type,
 			     uint32_t flags_, gs_texture_type type,
 			     bool gdiCompatible, bool nv12_)
 			     bool gdiCompatible, bool nv12_)
 	: gs_texture(device, gs_type::gs_texture_2d, type, levels, colorFormat),
 	: gs_texture(device, gs_type::gs_texture_2d, type, levels, colorFormat),

+ 252 - 0
libobs-d3d11/d3d11-texture3d.cpp

@@ -0,0 +1,252 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <util/base.h>
+#include "d3d11-subsystem.hpp"
+
+void gs_texture_3d::InitSRD(vector<D3D11_SUBRESOURCE_DATA> &srd)
+{
+	uint32_t rowSizeBits = width * gs_get_format_bpp(format);
+	uint32_t sliceSizeBytes = height * rowSizeBits / 8;
+	uint32_t actual_levels = levels;
+
+	if (!actual_levels)
+		actual_levels = gs_get_total_levels(width, height, depth);
+
+	uint32_t newRowSize = rowSizeBits / 8;
+	uint32_t newSlizeSize = sliceSizeBytes;
+
+	for (uint32_t level = 0; level < actual_levels; ++level) {
+		D3D11_SUBRESOURCE_DATA newSRD;
+		newSRD.pSysMem = data[level].data();
+		newSRD.SysMemPitch = newRowSize;
+		newSRD.SysMemSlicePitch = newSlizeSize;
+		srd.push_back(newSRD);
+
+		newRowSize /= 2;
+		newSlizeSize /= 4;
+	}
+}
+
+void gs_texture_3d::BackupTexture(const uint8_t *const *data)
+{
+	this->data.resize(levels);
+
+	uint32_t w = width;
+	uint32_t h = height;
+	uint32_t d = depth;
+	const uint32_t bbp = gs_get_format_bpp(format);
+
+	for (uint32_t i = 0; i < levels; i++) {
+		if (!data[i])
+			break;
+
+		const uint32_t texSize = bbp * w * h * d / 8;
+		this->data[i].resize(texSize);
+
+		vector<uint8_t> &subData = this->data[i];
+		memcpy(&subData[0], data[i], texSize);
+
+		if (w > 1)
+			w /= 2;
+		if (h > 1)
+			h /= 2;
+		if (d > 1)
+			d /= 2;
+	}
+}
+
+void gs_texture_3d::GetSharedHandle(IDXGIResource *dxgi_res)
+{
+	HANDLE handle;
+	HRESULT hr;
+
+	hr = dxgi_res->GetSharedHandle(&handle);
+	if (FAILED(hr)) {
+		blog(LOG_WARNING,
+		     "GetSharedHandle: Failed to "
+		     "get shared handle: %08lX",
+		     hr);
+	} else {
+		sharedHandle = (uint32_t)(uintptr_t)handle;
+	}
+}
+
+void gs_texture_3d::InitTexture(const uint8_t *const *data)
+{
+	HRESULT hr;
+
+	memset(&td, 0, sizeof(td));
+	td.Width = width;
+	td.Height = height;
+	td.Depth = depth;
+	td.MipLevels = genMipmaps ? 0 : levels;
+	td.Format = dxgiFormat;
+	td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+	td.CPUAccessFlags = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0;
+	td.Usage = isDynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+
+	if (type == GS_TEXTURE_CUBE)
+		td.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
+
+	if (isRenderTarget)
+		td.BindFlags |= D3D11_BIND_RENDER_TARGET;
+
+	if ((flags & GS_SHARED_KM_TEX) != 0)
+		td.MiscFlags |= D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
+	else if ((flags & GS_SHARED_TEX) != 0)
+		td.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
+
+	if (data) {
+		BackupTexture(data);
+		InitSRD(srd);
+	}
+
+	hr = device->device->CreateTexture3D(&td, data ? srd.data() : NULL,
+					     texture.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create 3D texture", hr);
+
+	if (isShared) {
+		ComPtr<IDXGIResource> dxgi_res;
+
+		texture->SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM);
+
+		hr = texture->QueryInterface(__uuidof(IDXGIResource),
+					     (void **)&dxgi_res);
+		if (FAILED(hr)) {
+			blog(LOG_WARNING,
+			     "InitTexture: Failed to query "
+			     "interface: %08lX",
+			     hr);
+		} else {
+			GetSharedHandle(dxgi_res);
+
+			if (flags & GS_SHARED_KM_TEX) {
+				ComPtr<IDXGIKeyedMutex> km;
+				hr = texture->QueryInterface(
+					__uuidof(IDXGIKeyedMutex),
+					(void **)&km);
+				if (FAILED(hr)) {
+					throw HRError("Failed to query "
+						      "IDXGIKeyedMutex",
+						      hr);
+				}
+
+				km->AcquireSync(0, INFINITE);
+				acquired = true;
+			}
+		}
+	}
+}
+
+void gs_texture_3d::InitResourceView()
+{
+	HRESULT hr;
+
+	memset(&resourceDesc, 0, sizeof(resourceDesc));
+	resourceDesc.Format = dxgiFormat;
+
+	resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+	resourceDesc.Texture3D.MostDetailedMip = 0;
+	resourceDesc.Texture3D.MipLevels = genMipmaps || !levels ? -1 : levels;
+
+	hr = device->device->CreateShaderResourceView(texture, &resourceDesc,
+						      shaderRes.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create resource view", hr);
+}
+
+void gs_texture_3d::InitRenderTargets()
+{
+	D3D11_RENDER_TARGET_VIEW_DESC rtv;
+	rtv.Format = dxgiFormat;
+	rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
+	rtv.Texture3D.MipSlice = 0;
+	rtv.Texture3D.WSize = 1;
+
+	for (UINT i = 0; i < 6; i++) {
+		rtv.Texture3D.FirstWSlice = i;
+		const HRESULT hr = device->device->CreateRenderTargetView(
+			texture, &rtv, renderTarget[i].Assign());
+		if (FAILED(hr))
+			throw HRError("Failed to create volume render "
+				      "target view",
+				      hr);
+	}
+}
+
+#define SHARED_FLAGS (GS_SHARED_TEX | GS_SHARED_KM_TEX)
+
+gs_texture_3d::gs_texture_3d(gs_device_t *device, uint32_t width,
+			     uint32_t height, uint32_t depth,
+			     gs_color_format colorFormat, uint32_t levels,
+			     const uint8_t *const *data, uint32_t flags_)
+	: gs_texture(device, gs_type::gs_texture_3d, GS_TEXTURE_3D, levels,
+		     colorFormat),
+	  width(width),
+	  height(height),
+	  depth(depth),
+	  flags(flags_),
+	  dxgiFormat(ConvertGSTextureFormat(format)),
+	  isRenderTarget((flags_ & GS_RENDER_TARGET) != 0),
+	  isDynamic((flags_ & GS_DYNAMIC) != 0),
+	  isShared((flags_ & SHARED_FLAGS) != 0),
+	  genMipmaps((flags_ & GS_BUILD_MIPMAPS) != 0),
+	  sharedHandle(GS_INVALID_HANDLE)
+{
+	InitTexture(data);
+	InitResourceView();
+
+	if (isRenderTarget)
+		InitRenderTargets();
+}
+
+gs_texture_3d::gs_texture_3d(gs_device_t *device, uint32_t handle)
+	: gs_texture(device, gs_type::gs_texture_3d, GS_TEXTURE_3D),
+	  isShared(true),
+	  sharedHandle(handle)
+{
+	HRESULT hr;
+	hr = device->device->OpenSharedResource((HANDLE)(uintptr_t)handle,
+						IID_PPV_ARGS(texture.Assign()));
+	if (FAILED(hr))
+		throw HRError("Failed to open shared 3D texture", hr);
+
+	texture->GetDesc(&td);
+
+	this->type = GS_TEXTURE_3D;
+	this->format = ConvertDXGITextureFormat(td.Format);
+	this->levels = 1;
+	this->device = device;
+
+	this->width = td.Width;
+	this->height = td.Height;
+	this->depth = td.Depth;
+	this->dxgiFormat = td.Format;
+
+	memset(&resourceDesc, 0, sizeof(resourceDesc));
+	resourceDesc.Format = td.Format;
+	resourceDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
+	resourceDesc.Texture3D.MostDetailedMip = 0;
+	resourceDesc.Texture3D.MipLevels = 1;
+
+	hr = device->device->CreateShaderResourceView(texture, &resourceDesc,
+						      shaderRes.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create shader resource view", hr);
+}

+ 1 - 0
libobs-opengl/CMakeLists.txt

@@ -58,6 +58,7 @@ set(libobs-opengl_SOURCES
 	gl-stagesurf.c
 	gl-stagesurf.c
 	gl-subsystem.c
 	gl-subsystem.c
 	gl-texture2d.c
 	gl-texture2d.c
+	gl-texture3d.c
 	gl-texturecube.c
 	gl-texturecube.c
 	gl-vertexbuffer.c
 	gl-vertexbuffer.c
 	gl-zstencil.c)
 	gl-zstencil.c)

+ 4 - 7
libobs-opengl/gl-helpers.c

@@ -45,13 +45,10 @@ bool gl_init_face(GLenum target, GLenum type, uint32_t num_levels,
 			data++;
 			data++;
 
 
 		size /= 4;
 		size /= 4;
-		width /= 2;
-		height /= 2;
-
-		if (width == 0)
-			width = 1;
-		if (height == 0)
-			height = 1;
+		if (width > 1)
+			width /= 2;
+		if (height > 1)
+			height /= 2;
 	}
 	}
 
 
 	if (data)
 	if (data)

+ 0 - 18
libobs-opengl/gl-subsystem.c

@@ -349,24 +349,6 @@ uint32_t device_get_height(const gs_device_t *device)
 	}
 	}
 }
 }
 
 
-gs_texture_t *device_voltexture_create(gs_device_t *device, uint32_t width,
-				       uint32_t height, uint32_t depth,
-				       enum gs_color_format color_format,
-				       uint32_t levels, const uint8_t **data,
-				       uint32_t flags)
-{
-	/* TODO */
-	UNUSED_PARAMETER(device);
-	UNUSED_PARAMETER(width);
-	UNUSED_PARAMETER(height);
-	UNUSED_PARAMETER(depth);
-	UNUSED_PARAMETER(color_format);
-	UNUSED_PARAMETER(levels);
-	UNUSED_PARAMETER(data);
-	UNUSED_PARAMETER(flags);
-	return NULL;
-}
-
 gs_samplerstate_t *
 gs_samplerstate_t *
 device_samplerstate_create(gs_device_t *device,
 device_samplerstate_create(gs_device_t *device,
 			   const struct gs_sampler_info *info)
 			   const struct gs_sampler_info *info)

+ 10 - 0
libobs-opengl/gl-subsystem.h

@@ -527,6 +527,16 @@ struct gs_texture_2d {
 	GLuint unpack_buffer;
 	GLuint unpack_buffer;
 };
 };
 
 
+struct gs_texture_3d {
+	struct gs_texture base;
+
+	uint32_t width;
+	uint32_t height;
+	uint32_t depth;
+	bool gen_mipmaps;
+	GLuint unpack_buffer;
+};
+
 struct gs_texture_cube {
 struct gs_texture_cube {
 	struct gs_texture base;
 	struct gs_texture base;
 
 

+ 20 - 10
libobs-opengl/gl-texture2d.c

@@ -26,7 +26,7 @@ static bool upload_texture_2d(struct gs_texture_2d *tex, const uint8_t **data)
 	bool success;
 	bool success;
 
 
 	if (!num_levels)
 	if (!num_levels)
-		num_levels = gs_get_total_levels(tex->width, tex->height);
+		num_levels = gs_get_total_levels(tex->width, tex->height, 1);
 
 
 	if (!gl_bind_texture(GL_TEXTURE_2D, tex->base.texture))
 	if (!gl_bind_texture(GL_TEXTURE_2D, tex->base.texture))
 		return false;
 		return false;
@@ -145,18 +145,25 @@ static inline bool is_texture_2d(const gs_texture_t *tex, const char *func)
 
 
 void gs_texture_destroy(gs_texture_t *tex)
 void gs_texture_destroy(gs_texture_t *tex)
 {
 {
-	struct gs_texture_2d *tex2d = (struct gs_texture_2d *)tex;
 	if (!tex)
 	if (!tex)
 		return;
 		return;
 
 
-	if (!is_texture_2d(tex, "gs_texture_destroy"))
-		return;
-
 	if (tex->cur_sampler)
 	if (tex->cur_sampler)
 		gs_samplerstate_destroy(tex->cur_sampler);
 		gs_samplerstate_destroy(tex->cur_sampler);
 
 
-	if (!tex->is_dummy && tex->is_dynamic && tex2d->unpack_buffer)
-		gl_delete_buffers(1, &tex2d->unpack_buffer);
+	if (!tex->is_dummy && tex->is_dynamic) {
+		if (tex->type == GS_TEXTURE_2D) {
+			struct gs_texture_2d *tex2d =
+				(struct gs_texture_2d *)tex;
+			if (tex2d->unpack_buffer)
+				gl_delete_buffers(1, &tex2d->unpack_buffer);
+		} else if (tex->type == GS_TEXTURE_3D) {
+			struct gs_texture_3d *tex3d =
+				(struct gs_texture_3d *)tex;
+			if (tex3d->unpack_buffer)
+				gl_delete_buffers(1, &tex3d->unpack_buffer);
+		}
+	}
 
 
 	if (tex->texture)
 	if (tex->texture)
 		gl_delete_textures(1, &tex->texture);
 		gl_delete_textures(1, &tex->texture);
@@ -253,19 +260,22 @@ failed:
 
 
 bool gs_texture_is_rect(const gs_texture_t *tex)
 bool gs_texture_is_rect(const gs_texture_t *tex)
 {
 {
-	const struct gs_texture_2d *tex2d = (const struct gs_texture_2d *)tex;
-	if (!is_texture_2d(tex, "gs_texture_unmap")) {
+	if (tex->type == GS_TEXTURE_3D)
+		return false;
+
+	if (!is_texture_2d(tex, "gs_texture_is_rect")) {
 		blog(LOG_ERROR, "gs_texture_is_rect (GL) failed");
 		blog(LOG_ERROR, "gs_texture_is_rect (GL) failed");
 		return false;
 		return false;
 	}
 	}
 
 
+	const struct gs_texture_2d *tex2d = (const struct gs_texture_2d *)tex;
 	return tex2d->base.gl_target == GL_TEXTURE_RECTANGLE;
 	return tex2d->base.gl_target == GL_TEXTURE_RECTANGLE;
 }
 }
 
 
 void *gs_texture_get_obj(gs_texture_t *tex)
 void *gs_texture_get_obj(gs_texture_t *tex)
 {
 {
 	struct gs_texture_2d *tex2d = (struct gs_texture_2d *)tex;
 	struct gs_texture_2d *tex2d = (struct gs_texture_2d *)tex;
-	if (!is_texture_2d(tex, "gs_texture_unmap")) {
+	if (!is_texture_2d(tex, "gs_texture_get_obj")) {
 		blog(LOG_ERROR, "gs_texture_get_obj (GL) failed");
 		blog(LOG_ERROR, "gs_texture_get_obj (GL) failed");
 		return NULL;
 		return NULL;
 	}
 	}

+ 181 - 0
libobs-opengl/gl-texture3d.c

@@ -0,0 +1,181 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "gl-subsystem.h"
+
+static bool gl_init_volume(GLenum type, uint32_t num_levels, GLenum format,
+			   GLint internal_format, bool compressed,
+			   uint32_t width, uint32_t height, uint32_t depth,
+			   const uint8_t *const **p_data)
+{
+	bool success = true;
+	const uint8_t *const *data = p_data ? *p_data : NULL;
+	uint32_t i;
+	const uint32_t bpp = gs_get_format_bpp(format);
+
+	for (i = 0; i < num_levels; i++) {
+		if (compressed) {
+			uint32_t mip_size = width * height * depth * bpp / 8;
+			glCompressedTexImage3D(GL_TEXTURE_3D, i,
+					       internal_format, width, height,
+					       depth, 0, mip_size,
+					       data ? *data : NULL);
+			if (!gl_success("glCompressedTexImage3D"))
+				success = false;
+
+		} else {
+			glTexImage3D(GL_TEXTURE_3D, i, internal_format, width,
+				     height, depth, 0, format, type,
+				     data ? *data : NULL);
+			if (!gl_success("glTexImage3D"))
+				success = false;
+		}
+
+		if (data)
+			data++;
+
+		if (width > 1)
+			width /= 2;
+		if (height > 1)
+			height /= 2;
+		if (depth > 1)
+			depth /= 2;
+	}
+
+	if (data)
+		*p_data = data;
+	return success;
+}
+
+static bool upload_texture_3d(struct gs_texture_3d *tex,
+			      const uint8_t *const *data)
+{
+	uint32_t num_levels = tex->base.levels;
+	bool compressed = gs_is_compressed_format(tex->base.format);
+	bool success;
+
+	if (!num_levels)
+		num_levels = gs_get_total_levels(tex->width, tex->height,
+						 tex->depth);
+
+	if (!gl_bind_texture(GL_TEXTURE_3D, tex->base.texture))
+		return false;
+
+	success = gl_init_volume(tex->base.gl_type, num_levels,
+				 tex->base.gl_format,
+				 tex->base.gl_internal_format, compressed,
+				 tex->width, tex->height, tex->depth, &data);
+
+	if (!gl_tex_param_i(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL,
+			    num_levels - 1))
+		success = false;
+	if (!gl_bind_texture(GL_TEXTURE_3D, 0))
+		success = false;
+
+	return success;
+}
+
+static bool create_pixel_unpack_buffer(struct gs_texture_3d *tex)
+{
+	GLsizeiptr size;
+	bool success = true;
+
+	if (!gl_gen_buffers(1, &tex->unpack_buffer))
+		return false;
+
+	if (!gl_bind_buffer(GL_PIXEL_UNPACK_BUFFER, tex->unpack_buffer))
+		return false;
+
+	size = tex->width * gs_get_format_bpp(tex->base.format);
+	if (!gs_is_compressed_format(tex->base.format)) {
+		size /= 8;
+		size = (size + 3) & 0xFFFFFFFC;
+		size *= tex->height;
+		size *= tex->depth;
+	} else {
+		size *= tex->height;
+		size *= tex->depth;
+		size /= 8;
+	}
+
+	glBufferData(GL_PIXEL_UNPACK_BUFFER, size, 0, GL_DYNAMIC_DRAW);
+	if (!gl_success("glBufferData"))
+		success = false;
+
+	if (!gl_bind_buffer(GL_PIXEL_UNPACK_BUFFER, 0))
+		success = false;
+
+	return success;
+}
+
+gs_texture_t *device_voltexture_create(gs_device_t *device, uint32_t width,
+				       uint32_t height, uint32_t depth,
+				       enum gs_color_format color_format,
+				       uint32_t levels,
+				       const uint8_t *const *data,
+				       uint32_t flags)
+{
+	struct gs_texture_3d *tex = bzalloc(sizeof(struct gs_texture_3d));
+	tex->base.device = device;
+	tex->base.type = GS_TEXTURE_3D;
+	tex->base.format = color_format;
+	tex->base.levels = levels;
+	tex->base.gl_format = convert_gs_format(color_format);
+	tex->base.gl_internal_format = convert_gs_internal_format(color_format);
+	tex->base.gl_type = get_gl_format_type(color_format);
+	tex->base.gl_target = GL_TEXTURE_3D;
+	tex->base.is_dynamic = (flags & GS_DYNAMIC) != 0;
+	tex->base.is_render_target = (flags & GS_RENDER_TARGET) != 0;
+	tex->base.is_dummy = (flags & GS_GL_DUMMYTEX) != 0;
+	tex->base.gen_mipmaps = (flags & GS_BUILD_MIPMAPS) != 0;
+	tex->width = width;
+	tex->height = height;
+	tex->depth = depth;
+
+	if (!gl_gen_textures(1, &tex->base.texture))
+		goto fail;
+
+	if (!tex->base.is_dummy) {
+		if (tex->base.is_dynamic && !create_pixel_unpack_buffer(tex))
+			goto fail;
+		if (!upload_texture_3d(tex, data))
+			goto fail;
+	} else {
+		if (!gl_bind_texture(GL_TEXTURE_3D, tex->base.texture))
+			goto fail;
+
+		bool compressed = gs_is_compressed_format(tex->base.format);
+		bool did_init = gl_init_volume(tex->base.gl_type, 1,
+					       tex->base.gl_format,
+					       tex->base.gl_internal_format,
+					       compressed, tex->width,
+					       tex->height, tex->depth, NULL);
+		did_init =
+			gl_tex_param_i(GL_TEXTURE_3D, GL_TEXTURE_MAX_LEVEL, 0);
+
+		bool did_unbind = gl_bind_texture(GL_TEXTURE_3D, 0);
+		if (!did_init || !did_unbind)
+			goto fail;
+	}
+
+	return (gs_texture_t *)tex;
+
+fail:
+	gs_texture_destroy((gs_texture_t *)tex);
+	blog(LOG_ERROR, "device_voltexture_create (GL) failed");
+	return NULL;
+}

+ 1 - 1
libobs-opengl/gl-texturecube.c

@@ -29,7 +29,7 @@ static inline bool upload_texture_cube(struct gs_texture_cube *tex,
 	uint32_t i;
 	uint32_t i;
 
 
 	if (!num_levels)
 	if (!num_levels)
-		num_levels = gs_get_total_levels(tex->size, tex->size);
+		num_levels = gs_get_total_levels(tex->size, tex->size, 1);
 
 
 	for (i = 0; i < 6; i++) {
 	for (i = 0; i < 6; i++) {
 		GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
 		GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;

+ 2 - 1
libobs/graphics/device-exports.h

@@ -52,7 +52,8 @@ device_cubetexture_create(gs_device_t *device, uint32_t size,
 EXPORT gs_texture_t *
 EXPORT gs_texture_t *
 device_voltexture_create(gs_device_t *device, uint32_t width, uint32_t height,
 device_voltexture_create(gs_device_t *device, uint32_t width, uint32_t height,
 			 uint32_t depth, enum gs_color_format color_format,
 			 uint32_t depth, enum gs_color_format color_format,
-			 uint32_t levels, const uint8_t **data, uint32_t flags);
+			 uint32_t levels, const uint8_t *const *data,
+			 uint32_t flags);
 EXPORT gs_zstencil_t *device_zstencil_create(gs_device_t *device,
 EXPORT gs_zstencil_t *device_zstencil_create(gs_device_t *device,
 					     uint32_t width, uint32_t height,
 					     uint32_t width, uint32_t height,
 					     enum gs_zstencil_format format);
 					     enum gs_zstencil_format format);

+ 1 - 1
libobs/graphics/graphics-internal.h

@@ -53,7 +53,7 @@ struct gs_exports {
 	gs_texture_t *(*device_voltexture_create)(
 	gs_texture_t *(*device_voltexture_create)(
 		gs_device_t *device, uint32_t width, uint32_t height,
 		gs_device_t *device, uint32_t width, uint32_t height,
 		uint32_t depth, enum gs_color_format color_format,
 		uint32_t depth, enum gs_color_format color_format,
-		uint32_t levels, const uint8_t **data, uint32_t flags);
+		uint32_t levels, const uint8_t *const *data, uint32_t flags);
 	gs_zstencil_t *(*device_zstencil_create)(
 	gs_zstencil_t *(*device_zstencil_create)(
 		gs_device_t *device, uint32_t width, uint32_t height,
 		gs_device_t *device, uint32_t width, uint32_t height,
 		enum gs_zstencil_format format);
 		enum gs_zstencil_format format);

+ 4 - 2
libobs/graphics/graphics.h

@@ -938,10 +938,12 @@ static inline bool gs_is_compressed_format(enum gs_color_format format)
 	return (format == GS_DXT1 || format == GS_DXT3 || format == GS_DXT5);
 	return (format == GS_DXT1 || format == GS_DXT3 || format == GS_DXT5);
 }
 }
 
 
-static inline uint32_t gs_get_total_levels(uint32_t width, uint32_t height)
+static inline uint32_t gs_get_total_levels(uint32_t width, uint32_t height,
+					   uint32_t depth)
 {
 {
 	uint32_t size = width > height ? width : height;
 	uint32_t size = width > height ? width : height;
-	uint32_t num_levels = 0;
+	size = size > depth ? size : depth;
+	uint32_t num_levels = 1;
 
 
 	while (size > 1) {
 	while (size > 1) {
 		size /= 2;
 		size /= 2;