当前位置: 首页 > news >正文

第四章-初始化Direct3D

首先我们需要一个错误检测和抛出机制

inline std::string ToString(const HRESULT& result)
{char buffer[256];sprintf_s(buffer, "error code : 0x%08X\n", result);return std::string(buffer);
}class MyException : public std::runtime_error
{
public:MyException(const HRESULT& result): std::runtime_error(ToString(result)), m_Result(result){}
private:HRESULT m_Result;
};inline std::string ThrowIfFailed(const HRESULT result)
{if (!SUCCEEDED(result)){throw MyException(result);}
}

下面是我的整体结构

#pragma once
#include <d3d12.h>
#include <dxgi1_6.h>
#include <d3dcompiler.h>
#include <wrl.h>
#include "../d3dx12.h"
#pragma comment(lib, "d3dcompiler.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d12.lib")
namespace REVIEW
{using Microsoft::WRL::ComPtr;class reviewDirect{public:void init();private:void createDevice();private:ComPtr<ID3D12Device> m_Device;};
}

创建设备

我们之前所说direct3D依赖与设备,比如创建命令队列等。设备代表着一个显示适配器,显示适配器是一种3D图形硬件。但是一个系统也能用软件显示来模拟3D图形硬件的功能。

HRESULT WINAPI D3D12CreateDevice(
_In_opt_ IUnknown* pAdapter,
D3D_FEATURE_LEVEL MinimumFeatureLevel,
_In_ REFIID riid, // Expected: ID3D12Device
_COM_Outptr_opt_ void** ppDevice );
  • pAdapter:指定了创建设备时所用的显示适配器,若将此参数设定为空指针,则使用主适配器。我们目前先设定主适配器
  • MinimumFeatureLevel:应用程序需要硬件所支持的最低功能级别,如果适配器不支持此功能级别,则设备创建失败。目前指定为D3D_FEATURE_LEVEL_11_0(即支持Direct3D 11的特性)
void reviewDirect::createDevice(const ComPtr<IDXGIFactory7>& factory){HRESULT hardwareResult = D3D12CreateDevice(nullptr,D3D_FEATURE_LEVEL_11_0,IID_PPV_ARGS(m_Device.GetAddressOf()));//如果默认的主适配器创建失败,回退到warp设备if (FAILED(hardwareResult)){ComPtr<IDXGIAdapter4> pAdapter;UINT i = 0;factory->EnumWarpAdapter(IID_PPV_ARGS(pAdapter.GetAddressOf()));ThrowIfFailed(D3D12CreateDevice(pAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_Device)));}}void reviewDirect::initBase()
{#if defined(DEBUG || defined(_DEBUG)//启动d3d12的调式层ComPtr<ID3D12Debug> pDebug;ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(pDebug.GetAddressOf())));pDebug->EnableDebugLayer();#endifComPtr<IDXGIFactory7> pFactory;ThrowIfFailed(CreateDXGIFactory(IID_PPV_ARGS(pFactory.GetAddressOf())));//尝试创建硬件设备createDevice(pFactory);
}
  • 为了进入调试模式,我们首先开启了调试层。随后,Direct3D便会开启额外的调试功能,并在错误发生时向VC++的输出窗口发送类似下面的调试信息

  • 当使用主适配器创建失败以后,程序将回退到软件适配器WARP设备(window advanced Rasterization Platform)

创建围栏并获取描述符的大小

创建好设备以后,就可以为cpu和gpu的同步创建围栏fence了。另外,之前我们说过pipeline使用计算机的资源都是用描述符去引用的,我们需要知道我们的设备可以创建多少个描述符

void reviewDirect::createFence()
{ThrowIfFailed(m_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(m_Fence.GetAddressOf())));
}void reviewDirect::calculateNumOfDescriptors()
{m_CbvSrvUavDescriptorSize = m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);m_RtvDescriptorSize = m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);m_DsvDescriptorSize = m_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
}

检测对4x MSAA质量级别的支持

void reviewDirect::checkMSAASupport()
{D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msaaQualityLevels;msaaQualityLevels.Format = mBackBufferFormat;msaaQualityLevels.SampleCount = 4;msaaQualityLevels.NumQualityLevels = 0;msaaQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;ThrowIfFailed(m_Device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,&msaaQualityLevels, sizeof(msaaQualityLevels)));m4xMssaaQuality = msaaQualityLevels.NumQualityLevels;assert(m4xMssaaQuality > 0 && "Unexpected MSAA quality level.");
}

创建命令队列和命令列表

void reviewDirect::createCommandQueueList()
{D3D12_COMMAND_QUEUE_DESC commandQueueDesc {};commandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;commandQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;ThrowIfFailed(m_Device->CreateCommandQueue(&commandQueueDesc, IID_PPV_ARGS(m_CommandQueue.GetAddressOf())));ThrowIfFailed(m_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_CommandAllocator.GetAddressOf())));ThrowIfFailed(m_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_CommandAllocator.Get(), nullptr, IID_PPV_ARGS(m_CommandList.GetAddressOf())));//nodeMask:指定命令列表关联的物理适配器节点,在多 GPU 系统中使用。单 GPU 系统一般设为 0。
// type:D3D12_COMMAND_LIST_TYPE 枚举类型,指定命令列表的类型。常见取值如下:
// D3D12_COMMAND_LIST_TYPE_DIRECT:直接命令列表,可包含所有类型的命令,常用于渲染操作。
// D3D12_COMMAND_LIST_TYPE_COMPUTE:计算命令列表,专门用于计算着色器相关的命令。
// D3D12_COMMAND_LIST_TYPE_COPY:复制命令列表,主要用于数据的复制操作。
// pCommandAllocator:指向 ID3D12CommandAllocator 对象的指针。命令分配器负责管理命令列表所需的内存,每个命令列表都需要关联一个命令分配器。
// pInitialState:指向 ID3D12PipelineState 对象的指针,用于指定命令列表的初始管线状态。如果不需要初始状态,可以传入 nullptr。
// riid:请求的命令列表接口的 IID(接口标识符),通常使用 IID_PPV_ARGS 宏来获取,例如 IID_PPV_ARGS(&commandList)。
// ppCommandList:用于接收创建好的命令列表对象的指针。
}

目前还不会发起任何的绘制命令,所以不会用到流水线状态对象

描述创建交换链

交换链用于交换帧,首先需要填写一份DXGI_SWAP_CHAIN_DESC结构实例

typedef struct DXGI_SWAP_CHAIN_DESC{DXGI_MODE_DESC BufferDesc;DXGI_SAMPLE_DESC SampleDesc;DXGI_USAGE BufferUsage;UINT BufferCount;HWND OutputWindow;BOOL Windowed;DXGI_SWAP_EFFECT SwapEffect;UINT Flags;} 	DXGI_SWAP_CHAIN_DESC;

BufferDesc

  • 类型:DXGI_MODE_DESC
  • 作用:描述后台缓冲区的显示模式,涵盖分辨率、刷新率、像素格式等信息。DXGI_MODE_DESC 结构体定义
typedef struct DXGI_MODE_DESC {UINT Width;UINT Height;DXGI_RATIONAL RefreshRate;DXGI_FORMAT Format;DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;DXGI_MODE_SCALING Scaling;
} DXGI_MODE_DESC;
  • WidthHeight:分别表示缓冲区的宽度和高度(以像素为单位)。
  • RefreshRate:表示屏幕的刷新率,是一个 DXGI_RATIONAL 类型,包含分子和分母,例如 60Hz 可表示为 {60, 1}
  • Format:指定像素格式,如 DXGI_FORMAT_R8G8B8A8_UNORM 代表 32 位的 RGBA 格式。
  • ScanlineOrdering:指定扫描线的排序方式。
  • Scaling:指定缩放模式。

SampleDesc

    • 类型:DXGI_SAMPLE_DESC
    • 作用:描述多重采样的相关信息。DXGI_SAMPLE_DESC 结构体定义如下:
typedef struct DXGI_SAMPLE_DESC {UINT Count;UINT Quality;
} DXGI_SAMPLE_DESC;
  • Count:表示每个像素的采样数量。
  • Quality:表示采样的质量级别。

BufferUsage

    • 类型:DXGI_USAGE
    • 作用:指定后台缓冲区的使用方式,可通过按位或操作组合多个标志。常见的标志有:
      • DXGI_USAGE_RENDER_TARGET_OUTPUT:表示缓冲区将用作渲染目标输出。
      • DXGI_USAGE_SHADER_INPUT:表示缓冲区可作为着色器的输入。

BufferCount

    • 类型:UINT
    • 作用:指定交换链中后台缓冲区的数量。通常为 2(双缓冲)或 3(三缓冲)。双缓冲模式下,一个缓冲区用于渲染,另一个用于显示;三缓冲模式可减少画面卡顿,提升流畅度。

OutputWindow

    • 类型:HWND
    • 作用:指定交换链要呈现到的窗口句柄。这是一个 Windows 窗口句柄,用于将渲染结果显示到指定的窗口中。

Windowed

    • 类型:BOOL
    • 作用:指定交换链是在窗口模式(TRUE)还是全屏模式(FALSE)下运行。

SwapEffect

    • 类型:DXGI_SWAP_EFFECT
    • 作用:指定交换链的呈现方式。常见的取值有:
      • DXGI_SWAP_EFFECT_DISCARD:每次呈现后丢弃旧的缓冲区内容,适用于大多数情况。
      • DXGI_SWAP_EFFECT_FLIP_DISCARD:使用翻转模型进行呈现,可提高性能,是推荐的方式。

Flags

    • 类型:UINT
    • 作用:指定交换链的额外标志,可通过按位或操作组合多个标志。例如 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 允许在窗口模式和全屏模式之间切换。
void reviewDirect::createSwapChain(const ComPtr<IDXGIFactory7>& factory)
{ComPtr<IDXGISwapChain> pSwapChain;DXGI_SWAP_CHAIN_DESC desc{};desc.BufferDesc.Width = m_Width;desc.BufferDesc.Height = m_Height;desc.BufferDesc.RefreshRate.Numerator = 60;desc.BufferDesc.Format = mBackBufferFormat;desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;desc.SampleDesc.Count = m4xMssaaQuality ? 4 : 1;desc.SampleDesc.Quality = m4xMssaaQuality ? m4xMssaaQuality - 1 : 0;desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;desc.BufferCount = m_SwapChainBufferCount;desc.OutputWindow = reviewWinApp::GetHwnd();desc.Windowed = true;desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;ThrowIfFailed(factory->CreateSwapChain(m_CommandQueue.Get(), &desc, pSwapChain.GetAddressOf()));pSwapChain.As(&m_SwapChain);}

pSwapChain 是一个 ComPtr<IDXGISwapChain> 类型的智能指针,m_SwapChain 可能是另一种类型的 ComPtr,比如 ComPtr<IDXGISwapChain3> 或者其他派生自 IDXGISwapChain 的接口类型。As 方法会尝试把 pSwapChain 所管理的 IDXGISwapChain 对象转换为 m_SwapChain 所对应的接口类型,若转换成功,m_SwapChain 就会持有转换后的接口指针。

创建描述符堆

我们的描述符需要通过创建描述符堆来存储,对此Direct3D 以ID3D12DescriptorHeap接口表示描述符堆,并用ID3D12Device::CreateDescriptorHeap方法来创建它。这里演示创建rtv的描述符堆(用来指向渲染目标资源)dsv(用来指向深度缓冲区资源)

typedef struct D3D12_DESCRIPTOR_HEAP_DESC
{
D3D12_DESCRIPTOR_HEAP_TYPE Type;
UINT NumDescriptors;
D3D12_DESCRIPTOR_HEAP_FLAGS Flags;
UINT NodeMask;
} 	D3D12_DESCRIPTOR_HEAP_DESC;
void reviewDirect::createDescriptorHeap()
{D3D12_DESCRIPTOR_HEAP_DESC rtvDesc{};rtvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;rtvDesc.NumDescriptors = m_SwapChainBufferCount;rtvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;rtvDesc.NodeMask = 0;D3D12_DESCRIPTOR_HEAP_DESC dsvDesc{};dsvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;dsvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;dsvDesc.NumDescriptors = 1;dsvDesc.NodeMask = 0;ThrowIfFailed(m_Device->CreateDescriptorHeap(&rtvDesc, IID_PPV_ARGS(m_RtvDescriptorHeap.GetAddressOf())));ThrowIfFailed(m_Device->CreateDescriptorHeap(&dsvDesc, IID_PPV_ARGS(m_DsvDescriptorHeap.GetAddressOf())));
}

书中用到的框架中有以下定义

UINT m_SwapChainBufferCount = 2;
int m_CurrentBackBufferIndex = 0;

其中m_CurrentBackBufferIndex是用来记录当前后台缓冲区的索引的,这样在交换渲染缓冲区和显示缓冲区的时候有用。

获取描述符

有了描述符堆以后,我们还需要通过描述符堆去访问所存的描述符,通过句柄来引用描述符,通过下面的方式来获取:

rtvhandle的获取,获取m_CurrentBackBufferIndex对应的描述符handle

D3D12_CPU_DESCRIPTOR_HANDLE reviewDirect::getCurrentBackBufferView() const
{return CD3DX12_CPU_DESCRIPTOR_HANDLE(m_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(),m_CurrentBackBufferIndex, m_RtvDescriptorSize);
}

dsvhandle获取

因为dsv目前也就一个描述符,所以可以直接获取

D3D12_CPU_DESCRIPTOR_HANDLE reviewDirect::getDepthStencilViewHandle() const
{return m_DsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
}

创建渲染目标视图

前面创建了描述符堆,描述符的获取,但是有两个问题,首先我们的描述符对应的资源没有创建,其次我们描述符没有绑定对应的资源

渲染目标视图是在swapchain中,因为swapchain管理的就是渲染目标

m_SwapChain->GetBuffer()

virtual HRESULT STDMETHODCALLTYPE GetBuffer( 
/* [in] */ UINT Buffer,
/* [annotation][in] */ 
_In_  REFIID riid,
/* [annotation][out][in] */ 
_COM_Outptr_  void **ppSurface) = 0;
  1. buffer:希望获得的特定的后台缓冲区的索引(有时后台缓冲区并不只一个,所以需要索引指明)
  2. riid:希望获得的id3d12Resource接口的COMID
  3. ppSurface:返回一个指向ID3D12Resource接口的指针,这便是希望获得的后台缓冲区

调用IDXGISwapchain::GetBuffer方法会增加相关后台缓冲区的COM引用计数,所以在每次使用后一定要将其释放。通过ComPtr可以自定的实现这一点

接下来,使用ID3D12Device::CreateRenderTargetView方法来为获取的后台缓冲区创建渲染目标视图(view)实际上也就是创建描述符handle/view 然后绑定后台缓冲区

m_Device->CreateRenderTargetView()

virtual void STDMETHODCALLTYPE CreateRenderTargetView( 
_In_opt_  ID3D12Resource *pResource,
_In_opt_  const D3D12_RENDER_TARGET_VIEW_DESC *pDesc,
_In_  D3D12_CPU_DESCRIPTOR_HANDLE DestDescriptor) = 0;

通过下面的方法就能为每个swapchain对应的buffer创建view(描述符handle)然后写入descriptor heap中

void reviewDirect::createRenderTargetView()
{CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(),m_CurrentBackBufferIndex, m_RtvDescriptorSize);for (UINT i = 0; i < m_SwapChainBufferCount; i++){ThrowIfFailed(m_SwapChain->GetBuffer(i, IID_PPV_ARGS(m_RenderBuffer[i].GetAddressOf())));m_Device->CreateRenderTargetView(m_RenderBuffer[i].Get(), nullptr, rtvHandle);rtvHandle.Offset(1, m_RtvDescriptorSize);}
}

pDesc 参数设置为 nullptr 时,DirectX 会根据资源的属性自动推断出渲染目标视图的配置,使用资源的默认设置来创建渲染目标视图。这里我们的swapchain已经设置好了其资源格式,就不需要再次创建了

创建深度/模版缓冲区及其视图

之前的swapchain得到的buffer实际上是一种资源, 之前所说深度缓冲区其实就是一种2D纹理。纹理是一种GPU资源因此我们需要填写D3D12_RESOURCE_DESC结构性来描述纹理资源,再用ID3D12Device::CreateCommitedResource方法来创建它。D3D12_RESOURCE_DESC结构体定义如下:

typedef struct D3D12_RESOURCE_DESC
{
D3D12_RESOURCE_DIMENSION Dimension;
UINT64 Alignment;
UINT64 Width;
UINT Height;
UINT16 DepthOrArraySize;
UINT16 MipLevels;
DXGI_FORMAT Format;
DXGI_SAMPLE_DESC SampleDesc;
D3D12_TEXTURE_LAYOUT Layout;
D3D12_RESOURCE_FLAGS Flags;
} 	D3D12_RESOURCE_DESC;
  1. Dimension资源纬度
typedef 
enum D3D12_RESOURCE_DIMENSION{D3D12_RESOURCE_DIMENSION_UNKNOWN	= 0,D3D12_RESOURCE_DIMENSION_BUFFER	= 1,D3D12_RESOURCE_DIMENSION_TEXTURE1D	= 2,D3D12_RESOURCE_DIMENSION_TEXTURE2D	= 3,D3D12_RESOURCE_DIMENSION_TEXTURE3D	= 4} 	D3D12_RESOURCE_DIMENSION;
  1. width,以纹素为单位来表示的纹理宽度。对于缓冲区资源,此项是缓冲区占用的字节数。height,以纹素为单位来表示的纹理高度

GPU的资源都存于堆(heap)中,其本质是具有特定的属性的GPU显存块。ID3D12Device::CreateCommitedResource方法将根据我们所提供的属性创建一个资源与一个堆,并将该资源提交到这个堆中。

virtual HRESULT STDMETHODCALLTYPE CreateCommittedResource( 
_In_  const D3D12_HEAP_PROPERTIES *pHeapProperties,
D3D12_HEAP_FLAGS HeapFlags,
_In_  const D3D12_RESOURCE_DESC *pDesc,
D3D12_RESOURCE_STATES InitialResourceState,
_In_opt_  const D3D12_CLEAR_VALUE *pOptimizedClearValue,
REFIID riidResource,
_COM_Outptr_opt_  void **ppvResource) = 0;typedef struct D3D12_HEAP_PROPERTIES{D3D12_HEAP_TYPE Type;D3D12_CPU_PAGE_PROPERTY CPUPageProperty;D3D12_MEMORY_POOL MemoryPoolPreference;UINT CreationNodeMask;UINT VisibleNodeMask;} 	D3D12_HEAP_PROPERTIES;
  1. pHeapProperties:(资源欲提交至的)堆所具有的属性。有一些属性是针对高级用法而设的。目前只需要关心type,而且这里可以用辅助函数来创建一个默认的。
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT)

type的类型包含

enum D3D12_HEAP_TYPE{//默认堆。向这堆里提交的资源,唯独GPU可以访问。比如GPU会读写深度模板缓冲区,但是CPU//不需要访问它,所以深度/模板缓冲区放默认堆中D3D12_HEAP_TYPE_DEFAULT	= 1,//上传堆。向此堆里提交的都是经CPU上传至GPU的资源D3D12_HEAP_TYPE_UPLOAD	= 2,//回读堆。向这种堆里提交的都是需要由CPU读取的资源D3D12_HEAP_TYPE_READBACK	= 3,//此成员应用于高级的场景D3D12_HEAP_TYPE_CUSTOM	= 4} 	D3D12_HEAP_TYPE;
  1. HeapFlags:与(资源欲提交至的)堆有关的额外选项标志。通常将它设为D3D12_HEAP_FLAG_NONE
  2. pDesc:指向一个D3D12_RESOURCE_DESC实例的指针,用它描述待建资源
  3. InitialResourceState资源的状态。不管什么时候,每个资源都会处于一种特定的使用状态。在资源创建时。需要用此参数来设置它的初始状态。对深度/缓冲区来说,通常设置为D3D12_RESOURCE_STATE_COMMON,在利用之前说的ResourceBarrier方法转换状态,比如转换为写的状态。
  4. pOptimizedClearValue:这个后续在渲染的时候会指定对应的值

void reviewDirect::createDepthStencilView()
{D3D12_RESOURCE_DESC desc{};desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;desc.Alignment = 0;desc.Width = m_Width;desc.Height = m_Height;desc.DepthOrArraySize = 1;desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;desc.SampleDesc.Count = m4xMssaaQuality ? 4 : 1;desc.SampleDesc.Quality = m4xMssaaQuality ? m4xMssaaQuality - 1 : 0;desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;D3D12_CLEAR_VALUE clearValue{};clearValue.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;clearValue.DepthStencil.Depth = 1.0f;clearValue.DepthStencil.Stencil = 0;ThrowIfFailed(m_Device->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),D3D12_HEAP_FLAG_NONE,&desc,D3D12_RESOURCE_STATE_COMMON,&clearValue,IID_PPV_ARGS(m_DepthStencilBuffer.GetAddressOf())));
}

这里创建了depth的buffer资源,但是我们需要对depth资源在渲染的时候写入,需要转换资源类型,这里就可以用到临时的commandlist 来处理,首先我先创建了一个templecommandallocator,这样就可以临时的创建多个commandlist,相当于一个临时的公司

void reviewDirect::createTempleCommandAllocator()
{ThrowIfFailed(m_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(m_TempleCommandAllocator.GetAddressOf())));
}

加上下面这段代码将缓冲区资源转为深度可写入的状态

ComPtr<ID3D12GraphicsCommandList> templeCommandList;
ThrowIfFailed(m_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_TempleCommandAllocator.Get(),nullptr, IID_PPV_ARGS(templeCommandList.GetAddressOf())));templeCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_DepthStencilBuffer.Get(), D3D12_RESOURCE_STATE_COMMON,D3D12_RESOURCE_STATE_DEPTH_WRITE));

注意这里就必须进行一次命令的执行,因为离开了createDepthStencilView()后临时的命令列表就会释放。这里创建了堵塞cpu的fence使用

void reviewDirect::waitForFinish()
{UINT fenceValue = m_FenceValue;ThrowIfFailed(m_CommandQueue->Signal(m_Fence.Get(), fenceValue));if (m_Fence->GetCompletedValue() < fenceValue){HANDLE handEvent = CreateEventEx(nullptr, nullptr, 0, EVENT_ALL_ACCESS);ThrowIfFailed(m_Fence->SetEventOnCompletion(fenceValue, handEvent));WaitForSingleObject(handEvent, INFINITE);CloseHandle(handEvent);}
}

我们还需要加上描述符来指向这个资源,完整的代码:


void reviewDirect::createDepthStencilView()
{D3D12_RESOURCE_DESC desc{};desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;desc.Alignment = 0;desc.Width = m_Width;desc.Height = m_Height;desc.DepthOrArraySize = 1;desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;desc.SampleDesc.Count = m4xMssaaQuality ? 4 : 1;desc.SampleDesc.Quality = m4xMssaaQuality ? m4xMssaaQuality - 1 : 0;desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;D3D12_CLEAR_VALUE clearValue{};clearValue.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;clearValue.DepthStencil.Depth = 1.0f;clearValue.DepthStencil.Stencil = 0;ThrowIfFailed(m_Device->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),D3D12_HEAP_FLAG_NONE,&desc,D3D12_RESOURCE_STATE_COMMON,&clearValue,IID_PPV_ARGS(m_DepthStencilBuffer.GetAddressOf())));CD3DX12_CPU_DESCRIPTOR_HANDLE viewHandle(m_DsvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());m_Device->CreateDepthStencilView(m_DepthStencilBuffer.Get(), nullptr, viewHandle);ComPtr<ID3D12GraphicsCommandList> templeCommandList;ThrowIfFailed(m_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_TempleCommandAllocator.Get(),nullptr, IID_PPV_ARGS(templeCommandList.GetAddressOf())));templeCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_DepthStencilBuffer.Get(), D3D12_RESOURCE_STATE_COMMON,D3D12_RESOURCE_STATE_DEPTH_WRITE));waitForFinish();ID3D12CommandList* commandLists[] = { templeCommandList.Get() };m_CommandQueue->ExecuteCommandLists(_countof(commandLists), commandLists);waitForFinish();
}

设置视口

我们通常会将3D场景绘制到与整个屏幕(在全屏模式下)或整个窗口工作区大小相当的后台缓冲区。但是,有时只是希望3D场景绘制到后台缓冲区的某个矩形子区域当中,如下图:

我们把后台缓冲区中的这种矩形子区域叫做视口,并通过下列结构描述它:

typedef struct D3D12_VIEWPORT
{
FLOAT TopLeftX;
FLOAT TopLeftY;
FLOAT Width;
FLOAT Height;
FLOAT MinDepth;
FLOAT MaxDepth;
} 	D3D12_VIEWPORT;
  1. FLOAT TopLeftX
    • 该成员表示视口矩形区域左上角的 X 坐标,以像素为单位。它指定了视口在屏幕上的水平起始位置。
  1. FLOAT TopLeftY
    • 此成员表示视口矩形区域左上角的 Y 坐标,同样以像素为单位。它指定了视口在屏幕上的垂直起始位置。
  1. FLOAT Width
    • 表示视口矩形区域的宽度,以像素为单位。这个值决定了视口在水平方向上的大小。
  1. FLOAT Height
    • 表示视口矩形区域的高度,以像素为单位。它决定了视口在垂直方向上的大小。
  1. FLOAT MinDepth
    • 定义了视口的最小深度值,范围通常是 [0.0f, 1.0f]。在深度测试时,只有深度值大于等于 MinDepth 的像素才会被考虑。
  1. FLOAT MaxDepth
    • 定义了视口的最大深度值,范围同样是 [0.0f, 1.0f]。在深度测试时,只有深度值小于等于 MaxDepth 的像素才会被考虑。
void reviewDirect::createViewPort()
{m_RenderViewPort.TopLeftX = 0;m_RenderViewPort.TopLeftY = 0;m_RenderViewPort.Width = m_Width;m_RenderViewPort.Height = m_Height;m_RenderViewPort.MinDepth = 0.0f;m_RenderViewPort.MaxDepth = 1.0f;
}

后续可以设置视口,这个我在后面在写

事实上,视口可以用作分屏游戏。首先创建两个视口,一个占屏幕左半部分,一个右半部分,接下来,在左视口中以玩家1的视角来绘制3D场景,在右视口中以玩家2的视角来绘制3D场景即可。

设置裁剪矩形

我们可以相对后台缓冲区定义一个裁剪矩形,在此矩形外的像素都将被剔除(即这些图像部分将不会被光栅化)至后台缓冲区。这个方法能用于优化程序的性能。例如,假设已知有一个矩形的UI元素覆盖于屏幕的某块区域的最上层,那么我们也就无须对覆盖区域绘制

void reviewDirect::createRECT()
{m_ScissorRect.left = 0;m_ScissorRect.top = 0;m_ScissorRect.right = m_Width;m_ScissorRect.bottom = m_Height;
}

相关文章:

第四章-初始化Direct3D

首先我们需要一个错误检测和抛出机制 inline std::string ToString(const HRESULT& result) {char buffer[256];sprintf_s(buffer, "error code : 0x%08X\n", result);return std::string(buffer); }class MyException : public std::runtime_error { public:My…...

实操3:6位数码管

文章目录 文章介绍仿真图原来的仿真代码教学用开发板段选和位选对应引脚思考题实物图 文章介绍 对应“案例5_3: 6位数码管显示0或者1【静态显示】” 跳转链接 要求&#xff1a;实现开发板的6位数码管同时显示0或者1 仿真图 原来的仿真代码 #include<reg52.h> // 头文件…...

常识补充(NVIDIA NVLink技术:打破GPU通信瓶颈的革命性互联技术)

文章目录 **引言&#xff1a;为什么需要NVLink&#xff1f;**1. NVLink技术概述1.1 什么是NVLink&#xff1f;1.2 NVLink的发展历程 2. NVLink vs. PCIe&#xff1a;关键对比2.1 带宽对比2.2 延迟对比 3. NVLink的架构与工作方式3.1 点对点直连&#xff08;P2P&#xff09;3.2 …...

openwrt 使用quilt 打补丁(patch)

1,引入 本文简单解释如何在OpenWRT下通过quilt命令打补丁--patch&#xff0c;也可查看openwrt官网提供的文档 2&#xff0c;以下代码通过编译net-snmp介绍 ① 执行编译命令之后&#xff0c;进入build_dir的net-snmp-5.9.1目录下&#xff0c;改目录即为snmp最终编译的目录了 /…...

NVIDIA Halos:智能汽车革命中的全栈式安全系统

高级辅助驾驶行业正面临一个尴尬的"安全悖论"——传感器数量翻倍的同时&#xff0c;事故率曲线却迟迟不见明显下降。究其原因&#xff0c;当前行业普遍存在三大技术困局&#xff1a; 碎片化安全方案 传统方案就像"打补丁"&#xff0c;激光雷达厂商只管点云…...

k8s术语之service

Kubernetes在设计之初就充分考虑了针对容器的服务发现与负载均衡机制&#xff0c;提供了Service资源&#xff0c;并通过kube-proxy配合cloud provider 来适应不同的用于场景。随着kubernetes用户的激增&#xff0c;用户场景的不断丰富&#xff0c;又产生了一些新的负载均衡机制…...

C/C++工程中的Plugin机制设计与Python实现

C/C工程中的Plugin机制设计与Python实现 1. Plugin机制设计概述 在C/C工程中实现Plugin机制通常需要以下几个关键组件&#xff1a; Plugin接口定义&#xff1a;定义统一的接口规范动态加载机制&#xff1a;运行时加载动态库注册机制&#xff1a;Plugin向主程序注册自己通信机…...

RNN 与 CNN:深度学习中的两大经典模型技术解析

在人工智能和深度学习领域,RNN(Recurrent Neural Network,循环神经网络) 和 CNN(Convolutional Neural Network,卷积神经网络) 是两种非常重要的神经网络结构。 它们分别擅长处理不同类型的数据,在自然语言处理、计算机视觉等多个领域中发挥着关键作用。 本文将从原理…...

多模态训练与微调

1.为什么多模态模型需要大规模预训练&#xff1f; 多模态模型需要大规模预训练的原因包括&#xff1a; (1)数据丰富性&#xff1a;大规模预训练可以暴露模型于丰富的数据&#xff0c;提升其泛化能力。 (2)特征提取&#xff1a;通过预训练&#xff0c;模型能够学习到有效的特…...

【HDLBits刷题】Verilog Language——1.Basics

目录 一、题目与题解 1.Simple wire&#xff08;简单导线&#xff09; 2.Four wires&#xff08;4线&#xff09; 3.Inverter&#xff08;逆变器&#xff08;非门&#xff09;&#xff09; 4.AND gate &#xff08;与门&#xff09; 5. NOR gate &#xff08;或非门&am…...

基于深度学习的图像识别技术:从原理到应用

前言 在当今数字化时代&#xff0c;图像识别技术已经渗透到我们生活的方方面面&#xff0c;从智能手机的人脸解锁功能到自动驾驶汽车对交通标志的识别&#xff0c;再到医疗影像诊断中的病变检测&#xff0c;图像识别技术正以其强大的功能和广泛的应用前景&#xff0c;改变着我们…...

【coze】手册小助手(提示词、知识库、交互、发布)

【coze】手册小助手&#xff08;提示词、知识库、交互、发布&#xff09; 1、创建智能体2、添加提示词3、创建知识库4、测试智能体5、添加交互功能6、发布智能体 1、创建智能体 2、添加提示词 # 角色 你是帮助用户搜索手册资料的AI助手 ## 工作流程 ### 步骤一:查询知识库 1.每…...

【教学类-34-11】20250506异形拼图块(圆形、三角、正方,椭圆/半圆)的中2班幼儿偏好性测试(HTML)

背景介绍 最近在写一份工具运用报告,关于剪纸难度的。所以设计了蝴蝶描边系列和异形凹凸角拼图。 【教学类-102-20】蝴蝶三色图作品2——卡纸蝴蝶“满格变形图”(滴颜料按压对称花纹、原图切边后变形放大到A4横版最大化)-CSDN博客文章浏览阅读609次,点赞8次,收藏3次。【…...

Debian系统上PostgreSQL15版本安装调试插件及DBeaver相应配置

PostgreSQL所在Debian Linux服务器安装插件程序 在PostgreSQL数据库服务器Debian系统上执行以下命令&#xff0c;安装插件pldebugger: sudo apt install postgresql-15-pldebugger #上面这一条命令运行完好像pgsql服务自动重启了&#xff0c;看日志的样子是这样的&#xff0c…...

GD32F470+CH395Q

tcp_client配置 第一步&#xff1a;资料下载 以太网协议栈芯片 CH395 - 南京沁恒微电子股份有限公司 第二步&#xff1a;准备工程 &#xff08;1&#xff09; 首先准备一个编译无报错、可以正常打印和延时的工程文件&#xff0c;官方例程采用STM32F1芯片&#xff0c;但本文…...

解决Hyper-V无法启动Debian 12虚拟机

问题 有时&#xff0c;我们会想要在Hyper-V中运行Debian12。我们想利用该系统的ISO镜像文件安装一个全新的虚拟机。 然而&#xff0c;当我们在Hyper-V中创建了一个2代虚拟机、添加了Debian 12的网络安装&#xff08;Netinst&#xff09;ISO作为最先启动的介质时&#xff0c;Hy…...

linux redis 设置密码以及redis拓展

redis拓展:http://pecl.php.net/package/redis 在服务器上&#xff0c;这里以linux服务器为例&#xff0c;为redis配置密码。 需要永久配置密码的话就去redis.conf的配置文件中找到requirepass这个参数&#xff0c;如下配置&#xff1a; 修改redis.conf配置文件 # requirepass …...

uniapp app 端获取陀螺仪数据的实现攻略

在 uniapp 开发中&#xff0c;uni.startGyroscope在 app 端并不被支持&#xff0c;这给需要获取陀螺仪数据的开发者带来了挑战。不过&#xff0c;借助 Native.js&#xff0c;我们能调用安卓原生代码实现这一需求。接下来&#xff0c;就为大家详细介绍实现步骤&#xff0c;并附上…...

第三节:Vben Admin 最新 v5.0 对接后端登录接口(下)

文章目录 前言一、处理请求头Authorization二、/auth/user/info 接口前端接口后端接口三、/auth/codes 接口1.前端2.后端四、测试接口前言 上一节内容,实现了登录的/auth/login 接口,但是登陆没有完成,还需要完成下面两个接口。才能完成登录。 一、处理请求头Authorizatio…...

标题:基于自适应阈值与K-means聚类的图像行列排序与拼接处理

摘要&#xff1a; 本文提出了一种基于自适应阈值和K-means聚类的图像行列排序与拼接方法。通过对灰度图像的自适应二值化处理&#xff0c;计算并分析图像的左右边距&#xff0c;从而确定图像的行数与列数。通过对图像进行特征提取&#xff0c;并使用K-means聚类进行排序&#…...

修改磁盘权限为管理员

1.右击需要修改的磁盘&#xff0c;点击属性 然后一路点击确定 已经修改好了...

P1782 旅行商的背包 Solution

Description 有一个体积为 C C C 的背包和若干种物品. 前 n n n 种物品&#xff0c;第 i i i 种体积为 v i v_i vi​&#xff0c;价值 w i w_i wi​&#xff0c;有 d i d_i di​ 件. 后 m m m 种物品&#xff0c;每种对应一个函数 f ( x ) a i x 2 b i x c i f(x)a…...

Acrel-EIoT 能源物联网云平台在能耗监测系统中的创新设计

摘要 随着能源管理的重要性日益凸显&#xff0c;能耗监测系统成为实现能源高效利用的关键手段。本文详细介绍了基于安科瑞Acrel-EIoT能源物联网云平台的能耗监测系统的设计架构与应用实践。该平台采用分层分布式结构&#xff0c;涵盖感知层、网络层、平台层和应用层&#xff0…...

乘法逆元【费马小定理+扩展欧几里得】

目录 模运算性质费马小定理乘法逆元扩展欧几里得算法随机栈 模运算性质 费马小定理 a,b互质&#xff1a;gcd(a,b)1 乘法逆元 a,b互质,满足a*x同余1(mod b),x是a模b的乘法逆元&#xff0c;记作a的-1次方。 扩展欧几里得算法 求axbygcd(a,b)的一组(x,y). 随机栈 题目来源&…...

Linux进程间通信(上)(21)

文章目录 前言一、什么是进程间通信&#xff1f;概念目的本质分类 二、管道什么是管道匿名管道匿名管道的原理pipe函数匿名管道使用步骤管道读写规则管道的特点管道的四种特殊情况管道的大小 总结 前言 本篇出得有点慢&#xff0c;因为我在这里更换了我的开发环境   不再使用…...

力扣面试150题--对称二叉树

Day 41 题目描述 做法 原理&#xff1a;拆分为根节点的左右两棵子树&#xff0c;比较左子树的右和右子树的左&#xff0c;左子树的左和右子树的右 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode righ…...

深度学习系统学习系列【6】之深度学习技巧

文章目录 数据集准备数据集扩展数据预处理1. 0均值&#xff08;Zero Centralization&#xff09;代码实现 2. 归一化&#xff08;Normalization&#xff09;代码实现 3. 主成分分析&#xff08;Principal Component Analysis, PCA&#xff09;实现步骤代码实现 4. 白化&#xf…...

vue项目中渲染markdown并处理报错

前言&#xff1a;想在vue项目中渲染markdown并处理报错问题 有以下几种方式&#xff1a; 1、使用第三方Markdown插件 2、通过Markdown转HTML工具 3、使用Vue组件处理Markdown 一、首先第一种&#xff1a;使用第三方Markdown插件 安装vue-markdown插件 或者 markdown-it&#xf…...

Vue3.5 企业级管理系统实战(十七):角色管理

本篇主要探讨角色管理功能&#xff0c;其中菜单权限这里先不实现&#xff0c;后续在菜单管理中再进行实现。接口部分依然是使用 Apifox mock 的。 1 角色 api 在 src/api/role.ts 中添加角色相关 api&#xff0c;代码如下&#xff1a; //src/api/role.ts import service fro…...

【AI论文】FormalMATH:大型语言模型形式化数学推理能力基准测试

摘要&#xff1a;正式的数学推理仍然是人工智能面临的一个关键挑战&#xff0c;受到现有基准在范围和规模上的限制。 为了解决这个问题&#xff0c;我们提出了FormalMATH&#xff0c;这是一个大规模的Lean4基准&#xff0c;包含5560个经过形式验证的问题&#xff0c;这些问题涵…...

9-4 USART串口数据包

HEX数据包的接收 研究几个小问题 1.包头包尾和数据载荷重复的问题 这里定义FF为包头&#xff0c;FE为包尾&#xff0c;如果我传输的数据本身就是FF和FE怎么呢&#xff1f;那这个问题确实存在&#xff0c;如果数据和包头包尾重复&#xff0c;可能会引起误判。我们有以下几种解…...

Babylon.js学习之路《 前言:为什么要学习Babylon.js 》

文章目录 引言&#xff1a;3D 开发在 Web 中的崛起为什么需要 Web 3D 开发&#xff1f;当选火热的应用场景数据表达方式的改变 Web 3D 的独特优势跨平台与零安装开发成本低即时更新与传播便捷 WebGL 的定位与挑战WebGL 是什么&#xff1f;WebGL 的直接使用痛点 为什么需要 Baby…...

今年我国已发生三级以上地震318次

快科技5月6日消息&#xff0c;根据中国地震台网的统计&#xff0c;今年以来&#xff08;截至4月30日&#xff09;&#xff0c;我国共发生三级以上地震318次&#xff0c;其中3.0-3.9级248次&#xff0c;4.0-4.9级61次&#xff0c;5.0-5.9级7次&#xff0c;6.0-6.9级2次&#xff…...

在与大语言模型交互中的礼貌现象:技术影响、社会行为与文化意义的多维度探讨

概述 关于是否值得对 AI 保持礼貌的公众意见&#xff0c;几乎和咖啡或红酒的最新研究结果一样频繁变化——这个月被推崇备至&#xff0c;下个月又受到质疑。即便如此&#xff0c;越来越多的用户现在在提示语中加入“请”或“谢谢”&#xff0c;这不仅仅是因为习惯&#xff0c;…...

Java后端开发day42--IO流(二)--字符集字符流

&#xff08;以下内容全部来自上述课程&#xff09; 拓展. try…catch异常处理&#xff08;能看懂&#xff09; 接口&#xff1a;AutoCloseable 特点&#xff1a;特定的情况下&#xff0c;可以自动释放资源 注意&#xff1a;只有实现了AutoCloseable接口的类&#xff0c;才能…...

【HarmonyOS 5】鸿蒙发展历程

【HarmonyOS 5】鸿蒙发展历程 一、鸿蒙 HarmonyOS 版本年代记 鸿蒙 1.0&#xff1a; 2019 年 8 月 9 日&#xff0c;华为在开发者大会上正式发布鸿蒙 1.0 系统&#xff0c;这一版本首次应用于华为荣耀智慧屏产品中&#xff0c;标志着华为正式进军操作系统领域。该版本初步展现…...

使用蚁群算法求解VRPTW问题

这里写目录标题 蚁群优化算法Python实现ACO求解VRPTW问题Java实现ACO求解VRPTW问题蚁群优化算法 蚁群算法(ACO)适合求解带时间窗的车辆路径优化问题(VRPTW),主要基于其仿生智能机制与问题特性的深度契合,具体体现在以下六个方面: 时间窗约束的自然映射 信息素导向与时间…...

内存的位运算

示例&#xff1a;提取和设置标志位 假设我们有一个32位的整数&#xff0c;其中不同的位代表不同的标志。例如&#xff1a; 位0&#xff1a;是否开启日志&#xff08;0表示关闭&#xff0c;1表示开启&#xff09; 位1&#xff1a;是否启用调试模式&#xff08;0表示禁用&#…...

高性能网络优化:深入解析忙轮询(Busy Polling)技术

在现代高性能网络应用中,如何降低数据包处理延迟、提升吞吐量是开发者与系统工程师的核心挑战之一。传统的“中断驱动”模式在高负载场景下表现不佳,而忙轮询(Busy Polling) 作为一种优化技术,通过主动轮询机制显著改善网络性能。本文将从原理、实现到实践,全面解析忙轮询…...

Linux grep 命令详解及示例大全

文章目录 一、基本语法二、常用选项及示例1. 基本匹配&#xff1a;查找包含某字符串的行2. 忽略大小写匹配 -i3. 显示行号 -n4. 递归查找目录下的文件 -r 或 -R5. 仅显示匹配的字符串 -o6. 使用正则表达式 -E&#xff08;扩展&#xff09;或 egrep7. 显示匹配前后行 -A, -B, -C…...

前端知识-hook

React 的生命周期管理被称为 Hook 技术&#xff0c;源于其设计哲学与实现机制中“钩入”组件运行流程的特性。这一命名既是对传统编程中“钩子”&#xff08;Hook&#xff09;概念的延伸&#xff0c;也体现了 React 对函数式组件的逻辑注入能力。以下从多个维度解析其关联性&am…...

uv全功能更新:统一管理Python项目、工具、脚本和环境的终极解决方案

花下猫语&#xff1a;uv 项目自发布起就大受欢迎&#xff0c;目前 Github star 52.6 K&#xff0c;远超过它的同类竞品们。前不久&#xff0c;它的创始人在 X 上披露了一组惊人的数据&#xff1a;uv 曾占据了 PyPI 超过 20% 的流量&#xff0c;用户每天通过它发起约 4-5 亿次下…...

Redis 使用及命令操作

文章目录 一、基本命令二、redis 设置键的生存时间或过期时间三、SortSet 排序集合类型操作四、查看中文五、密码设置和查看密码的方法六、关于 Redis 的 database 相关基础七、查看内存占用 一、基本命令 # 查看版本 redis-cli --version 结果&#xff1a;redis-cli 8.0.0red…...

ROS2:自定义接口文件(无废话)

目录 一、ROS2接口文件定义二、创建接口文件步骤三、验证是否创建成功&#xff0c;以及自定义接口文件的使用 一、ROS2接口文件定义 ROS2中接口文件的格式根据通信的类型可以分为三种&#xff1a; 话题通信&#xff1a;.msg文件 常用格式为&#xff1a;[消息类型] 消息名称 #话…...

如何配置 VScode 断点调试Linux 工程代码

1、Windowns 安装WSL 环境 2、VSCode 中 安装 Romote-SSH扩展&#xff0c;进行连接到WSL下的Linux 环境 安装Romote-SSH成功后&#xff0c;在左下角显示 &#xff0c; 点击此图标 出现 “连接到WSL”, 进行连接 显示&#xff0c;则表明链接成功 3、 VSCode 安装 C/C扩展的调试…...

tinyrenderer笔记(Phong光照模型)

tinyrenderer个人代码仓库&#xff1a;tinyrenderer个人练习代码 前言 在前面的渲染中&#xff0c;我们读取模型的 diffuse 纹理&#xff0c;然后根据法线计算模型的颜色。这次我们引入一种新的光照模型—— Phong 光照模型&#xff0c;Phong 光照模型将光照分为了三类&#x…...

Twin Builder 中的电池等效电路模型仿真

电池单元热设计挑战 电池热管理的主要挑战之一是确保温度低于最大工作限值。较高的温度会导致效率降低、加速老化和潜在的安全隐患。工程师必须了解电池产生的热量&#xff0c;才能充分设计冷却系统。 了解和预测电池模块的热行为需要将电池的热损耗与电池单元的电气机械特性…...

SQLark可以支持PostgreSQL了,有哪些新功能?

SQLark&#xff08;百灵连接&#xff09;是一款国产的数据库开发和管理工具&#xff0c;用于快速查询、创建和管理不同类型的数据库系统&#xff0c;支持达梦、Oracle 和 MySQL 数据库。 最新发布的 SQLark V3.4 版本新增了对 PostgreSQL 数据库的支持。我试用了一下&#xff…...

Redis 7.0中5种新特性及实战应用

Redis 7.0引入了多项革命性的新特性&#xff0c;不仅在性能和可靠性方面有所提升&#xff0c;更在功能和使用体验上有了质的飞跃。本文将介绍Redis 7.0的五大关键新特性&#xff0c;可以根据实际情况利用Redis 7.0的强大功能&#xff0c;构建更高效、更可靠的应用系统。 特性一…...

游戏如何应对AssetStudio解包工具

「游戏解包」是指将游戏文件中被压缩或加密的资源提取出来&#xff0c;通过解包工具对资源进行修改、查看或导出。这个过程通常涉及到将游戏客户端中的数据包进行解压&#xff0c;故称为“解包”。 游戏的资源文件包含代码、图片、视频、音频等重要内容。一旦被解密&#xff0…...