├── LICENSE
├── com
├── com_utils.go
└── iunknown_vtbl.go
├── d3d11
├── d3d11.go
├── d3d11_types.go
├── d3d11_vtbl.go
└── d3d11debug.go
├── dxgi
├── dxgi.go
├── dxgi_types.go
├── dxgi_types_string.go
└── dxgi_vtbl.go
├── examples
├── framelimiter
│ └── framelimiter.go
├── mjpegstream
│ ├── jpeg_native.go
│ ├── jpeg_turbo.go
│ └── main.go
└── recording
│ ├── main.go
│ └── transcode.go
├── go.mod
├── go.sum
├── hresult.go
├── hresult_string.go
├── outputduplication
├── output_duplication.go
└── swizzle
│ ├── LICENSE
│ ├── swizzle_amd64.go
│ ├── swizzle_amd64.s
│ ├── swizzle_common.go
│ ├── swizzle_other.go
│ └── swizzle_test.go
├── readme.md
└── win
├── syscall_windows.go
└── zsyscall_windows.go
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 kirides
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 | #
24 | # golang.org/x/exp/shiny/driver/internal/swizzle
25 | #
26 |
27 | Copyright (c) 2009 The Go Authors. All rights reserved.
28 |
29 | Redistribution and use in source and binary forms, with or without
30 | modification, are permitted provided that the following conditions are
31 | met:
32 |
33 | * Redistributions of source code must retain the above copyright
34 | notice, this list of conditions and the following disclaimer.
35 | * Redistributions in binary form must reproduce the above
36 | copyright notice, this list of conditions and the following disclaimer
37 | in the documentation and/or other materials provided with the
38 | distribution.
39 | * Neither the name of Google Inc. nor the names of its
40 | contributors may be used to endorse or promote products derived from
41 | this software without specific prior written permission.
42 |
43 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
44 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
45 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
46 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
47 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
48 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
49 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
50 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
51 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
52 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
53 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/com/com_utils.go:
--------------------------------------------------------------------------------
1 | package com
2 |
3 | import (
4 | "reflect"
5 | "syscall"
6 | "unsafe"
7 |
8 | "golang.org/x/sys/windows"
9 | )
10 |
11 | func ReflectQueryInterface(self interface{}, method uintptr, interfaceID *windows.GUID, obj interface{}) int32 {
12 | selfValue := reflect.ValueOf(self).Elem()
13 | objValue := reflect.ValueOf(obj).Elem()
14 |
15 | hr, _, _ := syscall.SyscallN(
16 | method,
17 | selfValue.UnsafeAddr(),
18 | uintptr(unsafe.Pointer(interfaceID)),
19 | objValue.Addr().Pointer())
20 |
21 | return int32(hr)
22 | }
23 |
--------------------------------------------------------------------------------
/com/iunknown_vtbl.go:
--------------------------------------------------------------------------------
1 | package com
2 |
3 | import "structs"
4 |
5 | type IUnknownVtbl struct {
6 | _ structs.HostLayout
7 | // every COM object starts with these three
8 | QueryInterface uintptr
9 | AddRef uintptr
10 | Release uintptr
11 | // _QueryInterface2 uintptr
12 | }
13 |
--------------------------------------------------------------------------------
/d3d11/d3d11.go:
--------------------------------------------------------------------------------
1 | package d3d11
2 |
3 | import (
4 | "fmt"
5 | "structs"
6 | "syscall"
7 | "unsafe"
8 |
9 | "golang.org/x/sys/windows"
10 |
11 | "github.com/kirides/go-d3d"
12 | "github.com/kirides/go-d3d/com"
13 | "github.com/kirides/go-d3d/dxgi"
14 | )
15 |
16 | var (
17 | modD3D11 = windows.NewLazySystemDLL("d3d11.dll")
18 | procD3D11CreateDevice = modD3D11.NewProc("D3D11CreateDevice")
19 |
20 | // iid_ID3D11Device1, _ = windows.GUIDFromString("{a04bfb29-08ef-43d6-a49c-a9bdbdcbe686}")
21 | IID_ID3D11Texture2D, _ = windows.GUIDFromString("{6f15aaf2-d208-4e89-9ab4-489535d34f9c}")
22 | IID_ID3D11Debug, _ = windows.GUIDFromString("{79cf2233-7536-4948-9d36-1e4692dc5760}")
23 | IID_ID3D11InfoQueue, _ = windows.GUIDFromString("{6543dbb6-1b48-42f5-ab82-e97ec74326f6}")
24 | )
25 |
26 | const (
27 | D3D11_USAGE_DEFAULT = 0
28 | D3D11_USAGE_STAGING = 3
29 |
30 | D3D11_CPU_ACCESS_READ = 0x20000
31 |
32 | D3D11_RLDO_SUMMARY = 0x1
33 | D3D11_RLDO_DETAIL = 0x2
34 | D3D11_RLDO_IGNORE_INTERNAL = 0x4
35 |
36 | D3D11_CREATE_DEVICE_DEBUG = 0x2
37 | D3D11_CREATE_DEVICE_BGRA_SUPPORT = 0x20
38 |
39 | D3D11_SDK_VERSION = 7
40 | )
41 |
42 | func _D3D11CreateDevice(ppDevice **ID3D11Device, ppDeviceContext **ID3D11DeviceContext) error {
43 | var factory1 *dxgi.IDXGIFactory1
44 | if err := dxgi.CreateDXGIFactory1(&factory1); err != nil {
45 | return fmt.Errorf("CreateDXGIFactory1: %w", err)
46 | }
47 | defer factory1.Release()
48 |
49 | var adapter1 *dxgi.IDXGIAdapter1
50 | if hr := factory1.EnumAdapters1(0, &adapter1); d3d.HRESULT(hr).Failed() {
51 | return fmt.Errorf("failed to enumerate desktop adapter. %w", d3d.HRESULT(hr))
52 | }
53 | defer adapter1.Release()
54 |
55 | fflags := [...]uint32{
56 | // 0xc100, // D3D_FEATURE_LEVEL_12_1
57 | // 0xc000, // D3D_FEATURE_LEVEL_12_0
58 | 0xb100, // D3D_FEATURE_LEVEL_11_1
59 | 0xb000, // D3D_FEATURE_LEVEL_11_0
60 | // 0xa100, // D3D_FEATURE_LEVEL_10_1
61 | // 0xa000, // D3D_FEATURE_LEVEL_10_0
62 | // 0x9300, // D3D_FEATURE_LEVEL_9_3
63 | // 0x9200, // D3D_FEATURE_LEVEL_9_2
64 | // 0x9100, // D3D_FEATURE_LEVEL_9_1
65 | // 0x1000, // D3D_FEATURE_LEVEL_1_0_CORE <-- unsupported!
66 | }
67 | featureLevel := 0x9100
68 | flags :=
69 | // D3D11_CREATE_DEVICE_DEBUG |
70 | 0
71 |
72 | ret, _, _ := syscall.SyscallN(
73 | procD3D11CreateDevice.Addr(),
74 | uintptr(unsafe.Pointer(adapter1)), // pAdapter
75 | uintptr(0), // driverType: 1 = Hardware
76 | uintptr(0), // software
77 | uintptr(flags), // flags
78 | uintptr(unsafe.Pointer(&fflags[0])), // supported feature levels
79 | uintptr(len(fflags)), // number of levels
80 | uintptr(D3D11_SDK_VERSION),
81 | uintptr(unsafe.Pointer(ppDevice)), // *D3D11Device
82 | uintptr(unsafe.Pointer(&featureLevel)), // feature level
83 | uintptr(unsafe.Pointer(ppDeviceContext)), // *D3D11DeviceContext
84 | )
85 |
86 | if ret != 0 {
87 | return d3d.HRESULT(ret)
88 | }
89 | return nil
90 | }
91 |
92 | func NewD3D11Device() (*ID3D11Device, *ID3D11DeviceContext, error) {
93 | var device *ID3D11Device
94 | var deviceCtx *ID3D11DeviceContext
95 |
96 | err := _D3D11CreateDevice(&device, &deviceCtx)
97 |
98 | if err != nil || device == nil || deviceCtx == nil {
99 | return nil, nil, err
100 | }
101 |
102 | return device, deviceCtx, nil
103 | }
104 |
105 | type ID3D11Texture2D struct {
106 | _ structs.HostLayout
107 | vtbl *ID3D11Texture2DVtbl
108 | }
109 |
110 | func (obj *ID3D11Texture2D) GetDesc(desc *D3D11_TEXTURE2D_DESC) int32 {
111 | ret, _, _ := syscall.SyscallN(
112 | obj.vtbl.GetDesc,
113 | uintptr(unsafe.Pointer(obj)),
114 | uintptr(unsafe.Pointer(desc)),
115 | )
116 | return int32(ret)
117 | }
118 | func (obj *ID3D11Texture2D) Release() int32 {
119 | ret, _, _ := syscall.SyscallN(
120 | obj.vtbl.Release,
121 | uintptr(unsafe.Pointer(obj)),
122 | )
123 | return int32(ret)
124 | }
125 | func (obj *ID3D11Texture2D) QueryInterface(iid windows.GUID, pp interface{}) int32 {
126 | return com.ReflectQueryInterface(obj, obj.vtbl.QueryInterface, &iid, pp)
127 | }
128 |
129 | type ID3D11Device struct {
130 | _ structs.HostLayout
131 | vtbl *ID3D11DeviceVtbl
132 | }
133 |
134 | func (obj *ID3D11Device) QueryInterface(iid windows.GUID, pp interface{}) int32 {
135 | return com.ReflectQueryInterface(obj, obj.vtbl.QueryInterface, &iid, pp)
136 | }
137 |
138 | func (obj *ID3D11Device) CreateTexture2D(desc *D3D11_TEXTURE2D_DESC, ppTexture2D **ID3D11Texture2D) int32 {
139 | ret, _, _ := syscall.SyscallN(
140 | obj.vtbl.CreateTexture2D,
141 | uintptr(unsafe.Pointer(obj)),
142 | uintptr(unsafe.Pointer(desc)),
143 | uintptr(0),
144 | uintptr(unsafe.Pointer(ppTexture2D)),
145 | )
146 | return int32(ret)
147 | }
148 |
149 | func (obj *ID3D11Device) Release() int32 {
150 | ret, _, _ := syscall.SyscallN(
151 | obj.vtbl.Release,
152 | uintptr(unsafe.Pointer(obj)),
153 | )
154 | return int32(ret)
155 | }
156 |
157 | type ID3D11Device1 struct {
158 | _ structs.HostLayout
159 | vtbl *ID3D11DeviceVtbl
160 | }
161 |
162 | func (obj *ID3D11Device1) Release() int32 {
163 | ret, _, _ := syscall.SyscallN(
164 | obj.vtbl.Release,
165 | uintptr(unsafe.Pointer(obj)),
166 | )
167 | return int32(ret)
168 | }
169 |
170 | func (obj *ID3D11Device1) CreateTexture2D(desc *D3D11_TEXTURE2D_DESC, ppTexture2D **ID3D11Texture2D) int32 {
171 | ret, _, _ := syscall.SyscallN(
172 | obj.vtbl.CreateTexture2D,
173 | uintptr(unsafe.Pointer(obj)),
174 | uintptr(unsafe.Pointer(desc)),
175 | uintptr(0),
176 | uintptr(unsafe.Pointer(ppTexture2D)),
177 | )
178 | return int32(ret)
179 | }
180 |
181 | type ID3D11DeviceContext struct {
182 | _ structs.HostLayout
183 | vtbl *ID3D11DeviceContextVtbl
184 | }
185 |
186 | func (obj *ID3D11DeviceContext) CopyResourceDXGI(dst, src *dxgi.IDXGIResource) int32 {
187 | ret, _, _ := syscall.SyscallN(
188 | obj.vtbl.CopyResource,
189 | uintptr(unsafe.Pointer(obj)),
190 | uintptr(unsafe.Pointer(dst)),
191 | uintptr(unsafe.Pointer(src)),
192 | )
193 | return int32(ret)
194 | }
195 | func (obj *ID3D11DeviceContext) CopyResource2D(dst, src *ID3D11Texture2D) int32 {
196 | ret, _, _ := syscall.SyscallN(
197 | obj.vtbl.CopyResource,
198 | uintptr(unsafe.Pointer(obj)),
199 | uintptr(unsafe.Pointer(dst)),
200 | uintptr(unsafe.Pointer(src)),
201 | )
202 | return int32(ret)
203 | }
204 | func (obj *ID3D11DeviceContext) CopySubresourceRegion2D(dst *ID3D11Texture2D, dstSubResource, dstX, dstY, dstZ uint32, src *ID3D11Texture2D, srcSubResource uint32, pSrcBox *D3D11_BOX) int32 {
205 | ret, _, _ := syscall.SyscallN(
206 | obj.vtbl.CopySubresourceRegion,
207 | uintptr(unsafe.Pointer(obj)),
208 | uintptr(unsafe.Pointer(dst)),
209 | uintptr(dstSubResource),
210 | uintptr(dstX),
211 | uintptr(dstY),
212 | uintptr(dstZ),
213 | uintptr(unsafe.Pointer(src)),
214 | uintptr(srcSubResource),
215 | uintptr(unsafe.Pointer(pSrcBox)),
216 | )
217 | return int32(ret)
218 | }
219 |
220 | func (obj *ID3D11DeviceContext) CopySubresourceRegion(dst *ID3D11Resource, dstSubResource, dstX, dstY, dstZ uint32, src *ID3D11Resource, srcSubResource uint32, pSrcBox *D3D11_BOX) int32 {
221 | ret, _, _ := syscall.SyscallN(
222 | obj.vtbl.CopySubresourceRegion,
223 | uintptr(unsafe.Pointer(obj)),
224 | uintptr(unsafe.Pointer(dst)),
225 | uintptr(dstSubResource),
226 | uintptr(dstX),
227 | uintptr(dstY),
228 | uintptr(dstZ),
229 | uintptr(unsafe.Pointer(src)),
230 | uintptr(srcSubResource),
231 | uintptr(unsafe.Pointer(pSrcBox)),
232 | )
233 | return int32(ret)
234 | }
235 | func (obj *ID3D11DeviceContext) Release() int32 {
236 | ret, _, _ := syscall.SyscallN(
237 | obj.vtbl.Release,
238 | uintptr(unsafe.Pointer(obj)),
239 | )
240 | return int32(ret)
241 | }
242 |
243 | type ID3D11Resource struct {
244 | _ structs.HostLayout
245 | vtbl *ID3D11ResourceVtbl
246 | }
247 |
248 | func (obj *ID3D11Resource) Release() int32 {
249 | ret, _, _ := syscall.SyscallN(
250 | obj.vtbl.Release,
251 | uintptr(unsafe.Pointer(obj)),
252 | )
253 | return int32(ret)
254 | }
255 |
--------------------------------------------------------------------------------
/d3d11/d3d11_types.go:
--------------------------------------------------------------------------------
1 | package d3d11
2 |
3 | import (
4 | "structs"
5 |
6 | "github.com/kirides/go-d3d/dxgi"
7 | )
8 |
9 | type D3D11_BOX struct {
10 | _ structs.HostLayout
11 |
12 | Left, Top, Front, Right, Bottom, Back uint32
13 | }
14 |
15 | type D3D11_TEXTURE2D_DESC struct {
16 | _ structs.HostLayout
17 |
18 | Width uint32
19 | Height uint32
20 | MipLevels uint32
21 | ArraySize uint32
22 | Format uint32
23 | SampleDesc dxgi.DXGI_SAMPLE_DESC
24 | Usage uint32
25 | BindFlags uint32
26 | CPUAccessFlags uint32
27 | MiscFlags uint32
28 | }
29 |
--------------------------------------------------------------------------------
/d3d11/d3d11_vtbl.go:
--------------------------------------------------------------------------------
1 | package d3d11
2 |
3 | import (
4 | "structs"
5 |
6 | "github.com/kirides/go-d3d/com"
7 | )
8 |
9 | type ID3D11DeviceChildVtbl struct {
10 | _ structs.HostLayout
11 |
12 | com.IUnknownVtbl
13 |
14 | GetDevice uintptr
15 | GetPrivateData uintptr
16 | SetPrivateData uintptr
17 | SetPrivateDataInterface uintptr
18 | }
19 |
20 | type ID3D11DeviceContextVtbl struct {
21 | _ structs.HostLayout
22 | ID3D11DeviceChildVtbl
23 |
24 | VSSetConstantBuffers uintptr
25 | PSSetShaderResources uintptr
26 | PSSetShader uintptr
27 | PSSetSamplers uintptr
28 | VSSetShader uintptr
29 | DrawIndexed uintptr
30 | Draw uintptr
31 | Map uintptr
32 | Unmap uintptr
33 | PSSetConstantBuffers uintptr
34 | IASetInputLayout uintptr
35 | IASetVertexBuffers uintptr
36 | IASetIndexBuffer uintptr
37 | DrawIndexedInstanced uintptr
38 | DrawInstanced uintptr
39 | GSSetConstantBuffers uintptr
40 | GSSetShader uintptr
41 | IASetPrimitiveTopology uintptr
42 | VSSetShaderResources uintptr
43 | VSSetSamplers uintptr
44 | Begin uintptr
45 | End uintptr
46 | GetData uintptr
47 | SetPredication uintptr
48 | GSSetShaderResources uintptr
49 | GSSetSamplers uintptr
50 | OMSetRenderTargets uintptr
51 | OMSetRenderTargetsAndUnorderedAccessViews uintptr
52 | OMSetBlendState uintptr
53 | OMSetDepthStencilState uintptr
54 | SOSetTargets uintptr
55 | DrawAuto uintptr
56 | DrawIndexedInstancedIndirect uintptr
57 | DrawInstancedIndirect uintptr
58 | Dispatch uintptr
59 | DispatchIndirect uintptr
60 | RSSetState uintptr
61 | RSSetViewports uintptr
62 | RSSetScissorRects uintptr
63 | CopySubresourceRegion uintptr
64 | CopyResource uintptr
65 |
66 | /// .....
67 | }
68 |
69 | type ID3D11DeviceVtbl struct {
70 | _ structs.HostLayout
71 | com.IUnknownVtbl
72 |
73 | CreateBuffer uintptr
74 | CreateTexture1D uintptr
75 | CreateTexture2D uintptr
76 | CreateTexture3D uintptr
77 | CreateShaderResourceView uintptr
78 | CreateUnorderedAccessView uintptr
79 | CreateRenderTargetView uintptr
80 | CreateDepthStencilView uintptr
81 | CreateInputLayout uintptr
82 | CreateVertexShader uintptr
83 | CreateGeometryShader uintptr
84 | CreateGeometryShaderWithStreamOutput uintptr
85 | CreatePixelShader uintptr
86 | CreateHullShader uintptr
87 | CreateDomainShader uintptr
88 | CreateComputeShader uintptr
89 | CreateClassLinkage uintptr
90 | CreateBlendState uintptr
91 | CreateDepthStencilState uintptr
92 | CreateRasterizerState uintptr
93 | CreateSamplerState uintptr
94 | CreateQuery uintptr
95 | CreatePredicate uintptr
96 | CreateCounter uintptr
97 | CreateDeferredContext uintptr
98 | OpenSharedResource uintptr
99 | CheckFormatSupport uintptr
100 | CheckMultisampleQualityLevels uintptr
101 | CheckCounterInfo uintptr
102 | CheckCounter uintptr
103 | CheckFeatureSupport uintptr
104 | GetPrivateData uintptr
105 | SetPrivateData uintptr
106 | SetPrivateDataInterface uintptr
107 | GetFeatureLevel uintptr
108 | GetCreationFlags uintptr
109 | GetDeviceRemovedReason uintptr
110 | GetImmediateContext uintptr
111 | SetExceptionMode uintptr
112 | GetExceptionMode uintptr
113 | }
114 |
115 | type ID3D11DebugVtbl struct {
116 | _ structs.HostLayout
117 | com.IUnknownVtbl
118 |
119 | SetFeatureMask uintptr
120 | GetFeatureMask uintptr
121 | SetPresentPerRenderOpDelay uintptr
122 | GetPresentPerRenderOpDelay uintptr
123 | SetSwapChain uintptr
124 | GetSwapChain uintptr
125 | ValidateContext uintptr
126 | ReportLiveDeviceObjects uintptr
127 | ValidateContextForDispatch uintptr
128 | }
129 |
130 | type ID3D11InfoQueueVtbl struct {
131 | _ structs.HostLayout
132 | com.IUnknownVtbl
133 |
134 | AddApplicationMessage uintptr
135 | AddMessage uintptr
136 | AddRetrievalFilterEntries uintptr
137 | AddStorageFilterEntries uintptr
138 | ClearRetrievalFilter uintptr
139 | ClearStorageFilter uintptr
140 | ClearStoredMessages uintptr
141 | GetBreakOnCategory uintptr
142 | GetBreakOnID uintptr
143 | GetBreakOnSeverity uintptr
144 | GetMessage uintptr
145 | GetMessageCountLimit uintptr
146 | GetMuteDebugOutput uintptr
147 | GetNumMessagesAllowedByStorageFilter uintptr
148 | GetNumMessagesDeniedByStorageFilter uintptr
149 | GetNumMessagesDiscardedByMessageCountLimit uintptr
150 | GetNumStoredMessages uintptr
151 | GetNumStoredMessagesAllowedByRetrievalFilter uintptr
152 | GetRetrievalFilter uintptr
153 | GetRetrievalFilterStackSize uintptr
154 | GetStorageFilter uintptr
155 | GetStorageFilterStackSize uintptr
156 | PopRetrievalFilter uintptr
157 | PopStorageFilter uintptr
158 | PushCopyOfRetrievalFilter uintptr
159 | PushCopyOfStorageFilter uintptr
160 | PushEmptyRetrievalFilter uintptr
161 | PushEmptyStorageFilter uintptr
162 | PushRetrievalFilter uintptr
163 | PushStorageFilter uintptr
164 | SetBreakOnCategory uintptr
165 | SetBreakOnID uintptr
166 | SetBreakOnSeverity uintptr
167 | SetMessageCountLimit uintptr
168 | SetMuteDebugOutput uintptr
169 | }
170 | type ID3D11ResourceVtbl struct {
171 | _ structs.HostLayout
172 | ID3D11DeviceChildVtbl
173 |
174 | GetType uintptr
175 | SetEvictionPriority uintptr
176 | GetEvictionPriority uintptr
177 | }
178 |
179 | type ID3D11Texture2DVtbl struct {
180 | _ structs.HostLayout
181 | ID3D11ResourceVtbl
182 |
183 | GetDesc uintptr
184 | }
185 |
--------------------------------------------------------------------------------
/d3d11/d3d11debug.go:
--------------------------------------------------------------------------------
1 | package d3d11
2 |
3 | import (
4 | "structs"
5 | "syscall"
6 | "unsafe"
7 |
8 | "github.com/kirides/go-d3d/com"
9 | "golang.org/x/sys/windows"
10 | )
11 |
12 | type ID3D11Debug struct {
13 | _ structs.HostLayout
14 | vtbl *ID3D11DebugVtbl
15 | }
16 |
17 | func (obj *ID3D11Debug) QueryInterface(iid windows.GUID, pp interface{}) int32 {
18 | return com.ReflectQueryInterface(obj, obj.vtbl.QueryInterface, &iid, pp)
19 | }
20 | func (obj *ID3D11Debug) ReportLiveDeviceObjects(flags uint32) int32 {
21 | ret, _, _ := syscall.Syscall(
22 | obj.vtbl.ReportLiveDeviceObjects,
23 | 2,
24 | uintptr(unsafe.Pointer(obj)),
25 | uintptr(flags),
26 | 0,
27 | )
28 | return int32(ret)
29 | }
30 | func (obj *ID3D11Debug) Release() int32 {
31 | ret, _, _ := syscall.Syscall(
32 | obj.vtbl.Release,
33 | 1,
34 | uintptr(unsafe.Pointer(obj)),
35 | 0,
36 | 0,
37 | )
38 | return int32(ret)
39 | }
40 |
41 | type ID3D11InfoQueue struct {
42 | _ structs.HostLayout
43 | vtbl *ID3D11InfoQueueVtbl
44 | }
45 |
46 | func (obj *ID3D11InfoQueue) Release() int32 {
47 | ret, _, _ := syscall.Syscall(
48 | obj.vtbl.Release,
49 | 1,
50 | uintptr(unsafe.Pointer(obj)),
51 | 0,
52 | 0,
53 | )
54 | return int32(ret)
55 | }
56 |
--------------------------------------------------------------------------------
/dxgi/dxgi.go:
--------------------------------------------------------------------------------
1 | package dxgi
2 |
3 | import (
4 | "structs"
5 | "syscall"
6 | "unsafe"
7 |
8 | "github.com/kirides/go-d3d"
9 | "github.com/kirides/go-d3d/com"
10 | "golang.org/x/sys/windows"
11 | )
12 |
13 | var (
14 | modDXGI = windows.NewLazySystemDLL("dxgi.dll")
15 | procCreateDXGIFactory1 = modDXGI.NewProc("CreateDXGIFactory1")
16 |
17 | // iid_IDXGIDevice, _ = windows.GUIDFromString("{54ec77fa-1377-44e6-8c32-88fd5f44c84c}")
18 | IID_IDXGIDevice1, _ = windows.GUIDFromString("{77db970f-6276-48ba-ba28-070143b4392c}")
19 | // IID_IDXGIAdapter, _ = windows.GUIDFromString("{2411E7E1-12AC-4CCF-BD14-9798E8534DC0}")
20 | IID_IDXGIAdapter1, _ = windows.GUIDFromString("{29038f61-3839-4626-91fd-086879011a05}")
21 | // IID_IDXGIOutput, _ = windows.GUIDFromString("{ae02eedb-c735-4690-8d52-5a8dc20213aa}")
22 | IID_IDXGIOutput1, _ = windows.GUIDFromString("{00cddea8-939b-4b83-a340-a685226666cc}")
23 | IID_IDXGIOutput5, _ = windows.GUIDFromString("{80A07424-AB52-42EB-833C-0C42FD282D98}")
24 | IID_IDXGIFactory1, _ = windows.GUIDFromString("{770aae78-f26f-4dba-a829-253c83d1b387}")
25 | // IID_IDXGIResource, _ = windows.GUIDFromString("{035f3ab4-482e-4e50-b41f-8a7f8bd8960b}")
26 | IID_IDXGISurface, _ = windows.GUIDFromString("{cafcb56c-6ac3-4889-bf47-9e23bbd260ec}")
27 | )
28 |
29 | const (
30 | DXGI_MAP_READ = 1 << 0
31 | DXGI_MAP_WRITE = 1 << 1
32 | DXGI_MAP_DISCARD = 1 << 2
33 | )
34 |
35 | type IDXGIFactory1 struct {
36 | _ structs.HostLayout
37 | vtbl *IDXGIFactory1Vtbl
38 | }
39 |
40 | func (obj *IDXGIFactory1) Release() int32 {
41 | ret, _, _ := syscall.SyscallN(
42 | obj.vtbl.Release,
43 | uintptr(unsafe.Pointer(obj)),
44 | )
45 | return int32(ret)
46 | }
47 |
48 | func (obj *IDXGIFactory1) EnumAdapters1(adapter uint32, pp **IDXGIAdapter1) int32 {
49 | ret, _, _ := syscall.SyscallN(
50 | obj.vtbl.EnumAdapters1,
51 | uintptr(unsafe.Pointer(obj)),
52 | uintptr(adapter),
53 | uintptr(unsafe.Pointer(pp)),
54 | )
55 | return int32(ret)
56 | }
57 |
58 | func CreateDXGIFactory1(ppFactory **IDXGIFactory1) error {
59 | ret, _, _ := syscall.SyscallN(
60 | procCreateDXGIFactory1.Addr(),
61 | uintptr(unsafe.Pointer(&IID_IDXGIFactory1)),
62 | uintptr(unsafe.Pointer(ppFactory)),
63 | )
64 | if ret != 0 {
65 | return d3d.HRESULT(ret)
66 | }
67 |
68 | return nil
69 | }
70 |
71 | type IDXGIAdapter1 struct {
72 | _ structs.HostLayout
73 | vtbl *IDXGIAdapter1Vtbl
74 | }
75 |
76 | func (obj *IDXGIAdapter1) Release() int32 {
77 | ret, _, _ := syscall.SyscallN(
78 | obj.vtbl.Release,
79 | uintptr(unsafe.Pointer(obj)),
80 | )
81 | return int32(ret)
82 | }
83 |
84 | func (obj *IDXGIAdapter1) EnumOutputs(output uint32, pp **IDXGIOutput) uint32 {
85 | ret, _, _ := syscall.SyscallN(
86 | obj.vtbl.EnumOutputs,
87 | uintptr(unsafe.Pointer(obj)),
88 | uintptr(output),
89 | uintptr(unsafe.Pointer(pp)),
90 | )
91 | return uint32(ret)
92 | }
93 |
94 | type IDXGIAdapter struct {
95 | _ structs.HostLayout
96 | vtbl *IDXGIAdapterVtbl
97 | }
98 |
99 | func (obj *IDXGIAdapter) EnumOutputs(output uint32, pp **IDXGIOutput) uint32 {
100 | ret, _, _ := syscall.SyscallN(
101 | obj.vtbl.EnumOutputs,
102 | uintptr(unsafe.Pointer(obj)),
103 | uintptr(output),
104 | uintptr(unsafe.Pointer(pp)),
105 | )
106 | return uint32(ret)
107 | }
108 |
109 | func (obj *IDXGIAdapter) Release() int32 {
110 | ret, _, _ := syscall.SyscallN(
111 | obj.vtbl.Release,
112 | uintptr(unsafe.Pointer(obj)),
113 | )
114 | return int32(ret)
115 | }
116 |
117 | type IDXGIDevice struct {
118 | _ structs.HostLayout
119 | vtbl *IDXGIDeviceVtbl
120 | }
121 |
122 | func (obj *IDXGIDevice) GetGPUThreadPriority(priority *int) int32 {
123 | ret, _, _ := syscall.SyscallN(
124 | obj.vtbl.GetGPUThreadPriority,
125 | uintptr(unsafe.Pointer(obj)),
126 | uintptr(unsafe.Pointer(priority)),
127 | )
128 | return int32(ret)
129 | }
130 | func (obj *IDXGIDevice) QueryInterface(iid windows.GUID, pp interface{}) int32 {
131 | return com.ReflectQueryInterface(obj, obj.vtbl.QueryInterface, &iid, pp)
132 | }
133 | func (obj *IDXGIDevice) GetParent(iid windows.GUID, pp *unsafe.Pointer) int32 {
134 | ret, _, _ := syscall.SyscallN(
135 | obj.vtbl.GetParent,
136 | uintptr(unsafe.Pointer(obj)),
137 | uintptr(unsafe.Pointer(&iid)),
138 | uintptr(unsafe.Pointer(pp)),
139 | )
140 | return int32(ret)
141 | }
142 | func (obj *IDXGIDevice) GetAdapter(pAdapter **IDXGIAdapter) int32 {
143 | ret, _, _ := syscall.SyscallN(
144 | obj.vtbl.GetAdapter,
145 | uintptr(unsafe.Pointer(obj)),
146 | uintptr(unsafe.Pointer(pAdapter)),
147 | )
148 | return int32(ret)
149 | }
150 | func (obj *IDXGIDevice) Release() int32 {
151 | ret, _, _ := syscall.SyscallN(
152 | obj.vtbl.Release,
153 | uintptr(unsafe.Pointer(obj)),
154 | )
155 | return int32(ret)
156 | }
157 |
158 | type IDXGIDevice1 struct {
159 | _ structs.HostLayout
160 | vtbl *IDXGIDevice1Vtbl
161 | }
162 |
163 | func (obj *IDXGIDevice1) QueryInterface(iid windows.GUID, pp interface{}) int32 {
164 | return com.ReflectQueryInterface(obj, obj.vtbl.QueryInterface, &iid, pp)
165 | }
166 |
167 | func (obj *IDXGIDevice1) GetParent(iid windows.GUID, pp *unsafe.Pointer) int32 {
168 | ret, _, _ := syscall.SyscallN(
169 | obj.vtbl.GetParent,
170 | uintptr(unsafe.Pointer(obj)),
171 | uintptr(unsafe.Pointer(&iid)),
172 | uintptr(unsafe.Pointer(pp)),
173 | )
174 |
175 | return int32(ret)
176 | }
177 | func (obj *IDXGIDevice1) GetAdapter(pAdapter *IDXGIAdapter) int32 {
178 | ret, _, _ := syscall.SyscallN(
179 | obj.vtbl.GetAdapter,
180 | uintptr(unsafe.Pointer(obj)),
181 | uintptr(unsafe.Pointer(&pAdapter)),
182 | )
183 |
184 | return int32(ret)
185 | }
186 | func (obj *IDXGIDevice1) Release() int32 {
187 | ret, _, _ := syscall.SyscallN(
188 | obj.vtbl.Release,
189 | uintptr(unsafe.Pointer(obj)),
190 | )
191 | return int32(ret)
192 | }
193 |
194 | type IDXGIOutput struct {
195 | _ structs.HostLayout
196 | vtbl *IDXGIOutputVtbl
197 | }
198 |
199 | func (obj *IDXGIOutput) QueryInterface(iid windows.GUID, pp interface{}) int32 {
200 | return com.ReflectQueryInterface(obj, obj.vtbl.QueryInterface, &iid, pp)
201 | }
202 |
203 | func (obj *IDXGIOutput) GetParent(iid windows.GUID, pp *unsafe.Pointer) int32 {
204 | ret, _, _ := syscall.SyscallN(
205 | obj.vtbl.GetParent,
206 | uintptr(unsafe.Pointer(obj)),
207 | uintptr(unsafe.Pointer(&iid)),
208 | uintptr(unsafe.Pointer(pp)),
209 | )
210 | return int32(ret)
211 | }
212 |
213 | func (obj *IDXGIOutput) Release() int32 {
214 | ret, _, _ := syscall.SyscallN(
215 | obj.vtbl.Release,
216 | uintptr(unsafe.Pointer(obj)),
217 | )
218 | return int32(ret)
219 | }
220 |
221 | type IDXGIOutput1 struct {
222 | _ structs.HostLayout
223 | vtbl *IDXGIOutput1Vtbl
224 | }
225 |
226 | func (obj *IDXGIOutput1) DuplicateOutput(device1 *IDXGIDevice1, ppOutputDuplication **IDXGIOutputDuplication) int32 {
227 | ret, _, _ := syscall.SyscallN(
228 | obj.vtbl.DuplicateOutput,
229 | uintptr(unsafe.Pointer(obj)),
230 | uintptr(unsafe.Pointer(device1)),
231 | uintptr(unsafe.Pointer(ppOutputDuplication)),
232 | )
233 | return int32(ret)
234 | }
235 |
236 | func (obj *IDXGIOutput1) GetParent(iid windows.GUID, pp *unsafe.Pointer) int32 {
237 | ret, _, _ := syscall.SyscallN(
238 | obj.vtbl.GetParent,
239 | uintptr(unsafe.Pointer(obj)),
240 | uintptr(unsafe.Pointer(&iid)),
241 | uintptr(unsafe.Pointer(pp)),
242 | )
243 | return int32(ret)
244 | }
245 |
246 | func (obj *IDXGIOutput1) Release() int32 {
247 | ret, _, _ := syscall.SyscallN(
248 | obj.vtbl.Release,
249 | uintptr(unsafe.Pointer(obj)),
250 | )
251 | return int32(ret)
252 | }
253 |
254 | type IDXGIOutput5 struct {
255 | _ structs.HostLayout
256 | vtbl *IDXGIOutput5Vtbl
257 | }
258 |
259 | type DXGI_FORMAT uint32
260 |
261 | func (obj *IDXGIOutput5) GetDesc(desc *DXGI_OUTPUT_DESC) int32 {
262 | ret, _, _ := syscall.SyscallN(
263 | obj.vtbl.GetDesc,
264 | uintptr(unsafe.Pointer(obj)),
265 | uintptr(unsafe.Pointer(desc)),
266 | )
267 | return int32(ret)
268 | }
269 |
270 | func (obj *IDXGIOutput5) DuplicateOutput1(device1 *IDXGIDevice1, flags uint32, pSupportedFormats []DXGI_FORMAT, ppOutputDuplication **IDXGIOutputDuplication) int32 {
271 | pFormats := &pSupportedFormats[0]
272 | ret, _, _ := syscall.SyscallN(
273 | obj.vtbl.DuplicateOutput1,
274 | uintptr(unsafe.Pointer(obj)),
275 | uintptr(unsafe.Pointer(device1)),
276 | uintptr(flags),
277 | uintptr(len(pSupportedFormats)),
278 | uintptr(unsafe.Pointer(pFormats)),
279 | uintptr(unsafe.Pointer(ppOutputDuplication)),
280 | )
281 | return int32(ret)
282 | }
283 |
284 | func (obj *IDXGIOutput5) GetParent(iid windows.GUID, pp *unsafe.Pointer) int32 {
285 | ret, _, _ := syscall.SyscallN(
286 | obj.vtbl.GetParent,
287 | uintptr(unsafe.Pointer(obj)),
288 | uintptr(unsafe.Pointer(&iid)),
289 | uintptr(unsafe.Pointer(pp)),
290 | )
291 | return int32(ret)
292 | }
293 |
294 | func (obj *IDXGIOutput5) Release() int32 {
295 | ret, _, _ := syscall.SyscallN(
296 | obj.vtbl.Release,
297 | uintptr(unsafe.Pointer(obj)),
298 | )
299 | return int32(ret)
300 | }
301 |
302 | type IDXGIResource struct {
303 | _ structs.HostLayout
304 | vtbl *IDXGIResourceVtbl
305 | }
306 |
307 | func (obj *IDXGIResource) QueryInterface(iid windows.GUID, pp interface{}) int32 {
308 | return com.ReflectQueryInterface(obj, obj.vtbl.QueryInterface, &iid, pp)
309 | }
310 | func (obj *IDXGIResource) Release() int32 {
311 | ret, _, _ := syscall.SyscallN(
312 | obj.vtbl.Release,
313 | uintptr(unsafe.Pointer(obj)),
314 | )
315 | return int32(ret)
316 | }
317 |
318 | type IDXGISurface struct {
319 | _ structs.HostLayout
320 | vtbl *IDXGISurfaceVtbl
321 | }
322 |
323 | func (obj *IDXGISurface) QueryInterface(iid windows.GUID, pp interface{}) int32 {
324 | return com.ReflectQueryInterface(obj, obj.vtbl.QueryInterface, &iid, pp)
325 | }
326 | func (obj *IDXGISurface) Map(pLockedRect *DXGI_MAPPED_RECT, mapFlags uint32) int32 {
327 | ret, _, _ := syscall.SyscallN(
328 | obj.vtbl.Map,
329 | uintptr(unsafe.Pointer(obj)),
330 | uintptr(unsafe.Pointer(pLockedRect)),
331 | uintptr(mapFlags),
332 | )
333 | return int32(ret)
334 | }
335 | func (obj *IDXGISurface) Unmap() int32 {
336 | ret, _, _ := syscall.SyscallN(
337 | obj.vtbl.Unmap,
338 | uintptr(unsafe.Pointer(obj)),
339 | )
340 | return int32(ret)
341 | }
342 | func (obj *IDXGISurface) Release() int32 {
343 | ret, _, _ := syscall.SyscallN(
344 | obj.vtbl.Release,
345 | uintptr(unsafe.Pointer(obj)),
346 | )
347 | return int32(ret)
348 | }
349 |
350 | type IDXGIOutputDuplication struct {
351 | _ structs.HostLayout
352 | vtbl *IDXGIOutputDuplicationVtbl
353 | }
354 |
355 | func (obj *IDXGIOutputDuplication) GetFrameMoveRects(buffer []DXGI_OUTDUPL_MOVE_RECT, rectsRequired *uint32) int32 {
356 | var buf *DXGI_OUTDUPL_MOVE_RECT
357 | if len(buffer) > 0 {
358 | buf = &buffer[0]
359 | }
360 | size := uint32(len(buffer) * 24)
361 | ret, _, _ := syscall.SyscallN(
362 | obj.vtbl.GetFrameMoveRects,
363 | uintptr(unsafe.Pointer(obj)),
364 | uintptr(size),
365 | uintptr(unsafe.Pointer(buf)),
366 | uintptr(unsafe.Pointer(rectsRequired)),
367 | )
368 | *rectsRequired = *rectsRequired / 24
369 | return int32(ret)
370 | }
371 | func (obj *IDXGIOutputDuplication) GetFrameDirtyRects(buffer []RECT, rectsRequired *uint32) int32 {
372 | var buf *RECT
373 | if len(buffer) > 0 {
374 | buf = &buffer[0]
375 | }
376 | size := uint32(len(buffer) * 16)
377 | ret, _, _ := syscall.SyscallN(
378 | obj.vtbl.GetFrameDirtyRects,
379 | uintptr(unsafe.Pointer(obj)),
380 | uintptr(size),
381 | uintptr(unsafe.Pointer(buf)),
382 | uintptr(unsafe.Pointer(rectsRequired)),
383 | )
384 | *rectsRequired = *rectsRequired / 16
385 | return int32(ret)
386 | }
387 |
388 | func (obj *IDXGIOutputDuplication) GetFramePointerShape(pointerShapeBufferSize uint32,
389 | pPointerShapeBuffer []byte,
390 | pPointerShapeBufferSizeRequired *uint32,
391 | pPointerShapeInfo *DXGI_OUTDUPL_POINTER_SHAPE_INFO) int32 {
392 |
393 | var buf *byte
394 | if len(pPointerShapeBuffer) > 0 {
395 | buf = &pPointerShapeBuffer[0]
396 | }
397 |
398 | ret, _, _ := syscall.SyscallN(
399 | obj.vtbl.GetFramePointerShape,
400 | uintptr(unsafe.Pointer(obj)),
401 | uintptr(pointerShapeBufferSize),
402 | uintptr(unsafe.Pointer(buf)),
403 | uintptr(unsafe.Pointer(pPointerShapeBufferSizeRequired)),
404 | uintptr(unsafe.Pointer(pPointerShapeInfo)),
405 | )
406 |
407 | return int32(ret)
408 | }
409 | func (obj *IDXGIOutputDuplication) GetDesc(desc *DXGI_OUTDUPL_DESC) int32 {
410 | ret, _, _ := syscall.SyscallN(
411 | obj.vtbl.GetDesc,
412 | uintptr(unsafe.Pointer(obj)),
413 | uintptr(unsafe.Pointer(desc)),
414 | )
415 | return int32(ret)
416 | }
417 |
418 | func (obj *IDXGIOutputDuplication) MapDesktopSurface(pLockedRect *DXGI_MAPPED_RECT) int32 {
419 | ret, _, _ := syscall.SyscallN(
420 | obj.vtbl.MapDesktopSurface,
421 | uintptr(unsafe.Pointer(obj)),
422 | uintptr(unsafe.Pointer(pLockedRect)),
423 | )
424 | return int32(ret)
425 | }
426 | func (obj *IDXGIOutputDuplication) UnMapDesktopSurface() int32 {
427 | ret, _, _ := syscall.SyscallN(
428 | obj.vtbl.UnMapDesktopSurface,
429 | uintptr(unsafe.Pointer(obj)),
430 | )
431 | return int32(ret)
432 | }
433 | func (obj *IDXGIOutputDuplication) AddRef() uint32 {
434 | ret, _, _ := syscall.SyscallN(
435 | obj.vtbl.AddRef,
436 | uintptr(unsafe.Pointer(obj)),
437 | )
438 | return uint32(ret)
439 | }
440 |
441 | func (obj *IDXGIOutputDuplication) Release() uint32 {
442 | ret, _, _ := syscall.SyscallN(
443 | obj.vtbl.Release,
444 | uintptr(unsafe.Pointer(obj)),
445 | )
446 | return uint32(ret)
447 | }
448 |
449 | func (obj *IDXGIOutputDuplication) AcquireNextFrame(timeoutMs uint32, pFrameInfo *DXGI_OUTDUPL_FRAME_INFO, ppDesktopResource **IDXGIResource) uint32 {
450 | ret, _, _ := syscall.SyscallN(
451 | obj.vtbl.AcquireNextFrame, // function address
452 | uintptr(unsafe.Pointer(obj)), // always pass the COM object address first
453 | uintptr(timeoutMs), // then all function parameters follow
454 | uintptr(unsafe.Pointer(pFrameInfo)),
455 | uintptr(unsafe.Pointer(ppDesktopResource)),
456 | )
457 | return uint32(ret)
458 | }
459 |
460 | func (obj *IDXGIOutputDuplication) ReleaseFrame() uint32 {
461 | ret, _, _ := syscall.SyscallN(
462 | obj.vtbl.ReleaseFrame,
463 | uintptr(unsafe.Pointer(obj)),
464 | )
465 | return uint32(ret)
466 | }
467 |
--------------------------------------------------------------------------------
/dxgi/dxgi_types.go:
--------------------------------------------------------------------------------
1 | package dxgi
2 |
3 | import "structs"
4 |
5 | //go:generate stringer -type=_DXGI_OUTDUPL_POINTER_SHAPE_TYPE -output=dxgi_types_string.go
6 |
7 | type DXGI_RATIONAL struct {
8 | _ structs.HostLayout
9 |
10 | Numerator uint32
11 | Denominator uint32
12 | }
13 |
14 | type DXGI_MODE_ROTATION uint32
15 |
16 | type DXGI_OUTPUT_DESC struct {
17 | _ structs.HostLayout
18 |
19 | DeviceName [32]uint16
20 | DesktopCoordinates RECT
21 | AttachedToDesktop uint32 // BOOL
22 | Rotation DXGI_MODE_ROTATION
23 | Monitor uintptr
24 | }
25 |
26 | type DXGI_MODE_DESC struct {
27 | _ structs.HostLayout
28 |
29 | Width uint32
30 | Height uint32
31 | Rational DXGI_RATIONAL
32 | Format uint32 // DXGI_FORMAT
33 | ScanlineOrdering uint32 // DXGI_MODE_SCANLINE_ORDER
34 | Scaling uint32 // DXGI_MODE_SCALING
35 | }
36 |
37 | type DXGI_OUTDUPL_DESC struct {
38 | _ structs.HostLayout
39 |
40 | ModeDesc DXGI_MODE_DESC
41 | Rotation uint32 // DXGI_MODE_ROTATION
42 | DesktopImageInSystemMemory uint32 // BOOL
43 | }
44 |
45 | type DXGI_SAMPLE_DESC struct {
46 | _ structs.HostLayout
47 |
48 | Count uint32
49 | Quality uint32
50 | }
51 |
52 | type POINT struct {
53 | _ structs.HostLayout
54 |
55 | X int32
56 | Y int32
57 | }
58 | type RECT struct {
59 | _ structs.HostLayout
60 |
61 | Left, Top, Right, Bottom int32
62 | }
63 |
64 | type DXGI_OUTDUPL_MOVE_RECT struct {
65 | _ structs.HostLayout
66 |
67 | Src POINT
68 | Dest RECT
69 | }
70 | type DXGI_OUTDUPL_POINTER_POSITION struct {
71 | _ structs.HostLayout
72 |
73 | Position POINT
74 | Visible uint32
75 | }
76 | type DXGI_OUTDUPL_FRAME_INFO struct {
77 | _ structs.HostLayout
78 |
79 | LastPresentTime int64
80 | LastMouseUpdateTime int64
81 | AccumulatedFrames uint32
82 | RectsCoalesced uint32
83 | ProtectedContentMaskedOut uint32
84 | PointerPosition DXGI_OUTDUPL_POINTER_POSITION
85 | TotalMetadataBufferSize uint32
86 | PointerShapeBufferSize uint32
87 | }
88 | type DXGI_MAPPED_RECT struct {
89 | _ structs.HostLayout
90 |
91 | Pitch int32
92 | PBits uintptr
93 | }
94 |
95 | const (
96 | DXGI_FORMAT_R8G8B8A8_UNORM DXGI_FORMAT = 28
97 | DXGI_FORMAT_B8G8R8A8_UNORM DXGI_FORMAT = 87
98 | )
99 |
100 | type DXGI_OUTDUPL_POINTER_SHAPE_TYPE uint32
101 |
102 | const (
103 | DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME DXGI_OUTDUPL_POINTER_SHAPE_TYPE = 1
104 | DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR DXGI_OUTDUPL_POINTER_SHAPE_TYPE = 2
105 | DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR DXGI_OUTDUPL_POINTER_SHAPE_TYPE = 4
106 | )
107 |
108 | type DXGI_OUTDUPL_POINTER_SHAPE_INFO struct {
109 | _ structs.HostLayout
110 |
111 | Type DXGI_OUTDUPL_POINTER_SHAPE_TYPE
112 | Width uint32
113 | Height uint32
114 | Pitch uint32
115 | HotSpot POINT
116 | }
117 |
--------------------------------------------------------------------------------
/dxgi/dxgi_types_string.go:
--------------------------------------------------------------------------------
1 | // Code generated by "stringer -type=_DXGI_OUTDUPL_POINTER_SHAPE_TYPE -output=dxgi_types_string.go"; DO NOT EDIT.
2 |
3 | package dxgi
4 |
5 | import "strconv"
6 |
7 | func _() {
8 | // An "invalid array index" compiler error signifies that the constant values have changed.
9 | // Re-run the stringer command to generate them again.
10 | var x [1]struct{}
11 | _ = x[DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME-1]
12 | _ = x[DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR-2]
13 | _ = x[DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR-4]
14 | }
15 |
16 | const (
17 | __DXGI_OUTDUPL_POINTER_SHAPE_TYPE_name_0 = "DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROMEDXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR"
18 | __DXGI_OUTDUPL_POINTER_SHAPE_TYPE_name_1 = "DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR"
19 | )
20 |
21 | var (
22 | __DXGI_OUTDUPL_POINTER_SHAPE_TYPE_index_0 = [...]uint8{0, 42, 79}
23 | )
24 |
25 | func (i DXGI_OUTDUPL_POINTER_SHAPE_TYPE) String() string {
26 | switch {
27 | case 1 <= i && i <= 2:
28 | i -= 1
29 | return __DXGI_OUTDUPL_POINTER_SHAPE_TYPE_name_0[__DXGI_OUTDUPL_POINTER_SHAPE_TYPE_index_0[i]:__DXGI_OUTDUPL_POINTER_SHAPE_TYPE_index_0[i+1]]
30 | case i == 4:
31 | return __DXGI_OUTDUPL_POINTER_SHAPE_TYPE_name_1
32 | default:
33 | return "_DXGI_OUTDUPL_POINTER_SHAPE_TYPE(" + strconv.FormatInt(int64(i), 10) + ")"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/dxgi/dxgi_vtbl.go:
--------------------------------------------------------------------------------
1 | package dxgi
2 |
3 | import (
4 | "structs"
5 |
6 | "github.com/kirides/go-d3d/com"
7 | )
8 |
9 | type IDXGIObjectVtbl struct {
10 | _ structs.HostLayout
11 | com.IUnknownVtbl
12 |
13 | SetPrivateData uintptr
14 | SetPrivateDataInterface uintptr
15 | GetPrivateData uintptr
16 | GetParent uintptr
17 | }
18 |
19 | type IDXGIAdapterVtbl struct {
20 | _ structs.HostLayout
21 | IDXGIObjectVtbl
22 |
23 | EnumOutputs uintptr
24 | GetDesc uintptr
25 | CheckInterfaceSupport uintptr
26 | }
27 | type IDXGIAdapter1Vtbl struct {
28 | _ structs.HostLayout
29 | IDXGIAdapterVtbl
30 |
31 | GetDesc1 uintptr
32 | }
33 |
34 | type IDXGIDeviceVtbl struct {
35 | _ structs.HostLayout
36 | IDXGIObjectVtbl
37 |
38 | CreateSurface uintptr
39 | GetAdapter uintptr
40 | GetGPUThreadPriority uintptr
41 | QueryResourceResidency uintptr
42 | SetGPUThreadPriority uintptr
43 | }
44 |
45 | type IDXGIDevice1Vtbl struct {
46 | _ structs.HostLayout
47 | IDXGIDeviceVtbl
48 |
49 | GetMaximumFrameLatency uintptr
50 | SetMaximumFrameLatency uintptr
51 | }
52 |
53 | type IDXGIDeviceSubObjectVtbl struct {
54 | _ structs.HostLayout
55 | IDXGIObjectVtbl
56 |
57 | GetDevice uintptr
58 | }
59 |
60 | type IDXGISurfaceVtbl struct {
61 | _ structs.HostLayout
62 | IDXGIDeviceSubObjectVtbl
63 |
64 | GetDesc uintptr
65 | Map uintptr
66 | Unmap uintptr
67 | }
68 |
69 | type IDXGIResourceVtbl struct {
70 | _ structs.HostLayout
71 | IDXGIDeviceSubObjectVtbl
72 |
73 | GetSharedHandle uintptr
74 | GetUsage uintptr
75 | SetEvictionPriority uintptr
76 | GetEvictionPriority uintptr
77 | }
78 |
79 | type IDXGIOutputVtbl struct {
80 | _ structs.HostLayout
81 | IDXGIObjectVtbl
82 |
83 | GetDesc uintptr
84 | GetDisplayModeList uintptr
85 | FindClosestMatchingMode uintptr
86 | WaitForVBlank uintptr
87 | TakeOwnership uintptr
88 | ReleaseOwnership uintptr
89 | GetGammaControlCapabilities uintptr
90 | SetGammaControl uintptr
91 | GetGammaControl uintptr
92 | SetDisplaySurface uintptr
93 | GetDisplaySurfaceData uintptr
94 | GetFrameStatistics uintptr
95 | }
96 |
97 | type IDXGIOutput1Vtbl struct {
98 | _ structs.HostLayout
99 | IDXGIOutputVtbl
100 |
101 | GetDisplayModeList1 uintptr
102 | FindClosestMatchingMode1 uintptr
103 | GetDisplaySurfaceData1 uintptr
104 | DuplicateOutput uintptr
105 | }
106 |
107 | type IDXGIOutput2Vtbl struct {
108 | _ structs.HostLayout
109 | IDXGIOutput1Vtbl
110 |
111 | SupportsOverlays uintptr
112 | }
113 |
114 | type IDXGIOutput3Vtbl struct {
115 | _ structs.HostLayout
116 | IDXGIOutput2Vtbl
117 |
118 | CheckOverlaySupport uintptr
119 | }
120 |
121 | type IDXGIOutput4Vtbl struct {
122 | _ structs.HostLayout
123 | IDXGIOutput3Vtbl
124 |
125 | CheckOverlayColorSpaceSupport uintptr
126 | }
127 | type IDXGIOutput5Vtbl struct {
128 | _ structs.HostLayout
129 | IDXGIOutput4Vtbl
130 |
131 | DuplicateOutput1 uintptr
132 | }
133 |
134 | type IDXGIOutputDuplicationVtbl struct {
135 | _ structs.HostLayout
136 | IDXGIObjectVtbl
137 |
138 | GetDesc uintptr
139 | AcquireNextFrame uintptr
140 | GetFrameDirtyRects uintptr
141 | GetFrameMoveRects uintptr
142 | GetFramePointerShape uintptr
143 | MapDesktopSurface uintptr
144 | UnMapDesktopSurface uintptr
145 | ReleaseFrame uintptr
146 | }
147 | type IDXGIFactoryVtbl struct {
148 | _ structs.HostLayout
149 | IDXGIObjectVtbl
150 |
151 | EnumAdapters uintptr
152 | MakeWindowAssociation uintptr
153 | GetWindowAssociation uintptr
154 | CreateSwapChain uintptr
155 | CreateSoftwareAdapter uintptr
156 | }
157 | type IDXGIFactory1Vtbl struct {
158 | _ structs.HostLayout
159 | IDXGIFactoryVtbl
160 |
161 | EnumAdapters1 uintptr
162 | IsCurrent uintptr
163 | }
164 |
--------------------------------------------------------------------------------
/examples/framelimiter/framelimiter.go:
--------------------------------------------------------------------------------
1 | package framelimiter
2 |
3 | import "time"
4 |
5 | // finer granularity for sleeping
6 | type frameLimiter struct {
7 | DesiredFps int
8 | frameTimeNs int64
9 |
10 | LastFrameTime time.Time
11 | LastSleepDuration time.Duration
12 |
13 | DidSleep bool
14 | DidSpin bool
15 | }
16 |
17 | func New(desiredFps int) *frameLimiter {
18 | return &frameLimiter{
19 | DesiredFps: desiredFps,
20 | frameTimeNs: (time.Second / time.Duration(desiredFps)).Nanoseconds(),
21 | LastFrameTime: time.Now(),
22 | }
23 | }
24 |
25 | func (l *frameLimiter) Wait() {
26 | l.DidSleep = false
27 | l.DidSpin = false
28 |
29 | now := time.Now()
30 | spinWaitUntil := now
31 |
32 | sleepTime := l.frameTimeNs - now.Sub(l.LastFrameTime).Nanoseconds()
33 |
34 | if sleepTime > int64(1*time.Millisecond) {
35 | if sleepTime < int64(30*time.Millisecond) {
36 | l.LastSleepDuration = time.Duration(sleepTime / 8)
37 | } else {
38 | l.LastSleepDuration = time.Duration(sleepTime / 4 * 3)
39 | }
40 | time.Sleep(time.Duration(l.LastSleepDuration))
41 | l.DidSleep = true
42 |
43 | newNow := time.Now()
44 | spinWaitUntil = newNow.Add(time.Duration(sleepTime) - newNow.Sub(now))
45 | now = newNow
46 |
47 | for spinWaitUntil.After(now) {
48 | now = time.Now()
49 | // SPIN WAIT
50 | l.DidSpin = true
51 | }
52 | } else {
53 | l.LastSleepDuration = 0
54 | spinWaitUntil = now.Add(time.Duration(sleepTime))
55 | for spinWaitUntil.After(now) {
56 | now = time.Now()
57 | // SPIN WAIT
58 | l.DidSpin = true
59 | }
60 | }
61 | l.LastFrameTime = time.Now()
62 | }
63 |
--------------------------------------------------------------------------------
/examples/mjpegstream/jpeg_native.go:
--------------------------------------------------------------------------------
1 | //go:build !turbo
2 |
3 | package main
4 |
5 | import (
6 | "image"
7 | "io"
8 |
9 | "image/jpeg"
10 | )
11 |
12 | func jpegQuality(q int) *jpeg.Options {
13 | return &jpeg.Options{Quality: q}
14 | }
15 |
16 | func encodeJpeg(w io.Writer, src image.Image, opts *jpeg.Options) {
17 | jpeg.Encode(w, src, opts)
18 | }
19 |
--------------------------------------------------------------------------------
/examples/mjpegstream/jpeg_turbo.go:
--------------------------------------------------------------------------------
1 | //go:build turbo
2 |
3 | package main
4 |
5 | import (
6 | "image"
7 | "io"
8 |
9 | "github.com/viam-labs/go-libjpeg/jpeg"
10 | )
11 |
12 | func jpegQuality(q int) *jpeg.EncoderOptions {
13 | return &jpeg.EncoderOptions{Quality: q}
14 | }
15 |
16 | func encodeJpeg(w io.Writer, src image.Image, opts *jpeg.EncoderOptions) {
17 | jpeg.Encode(w, src, opts)
18 | }
19 |
--------------------------------------------------------------------------------
/examples/mjpegstream/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "errors"
7 | "fmt"
8 | "image"
9 | "net/http"
10 | "runtime"
11 |
12 | "os"
13 | "os/signal"
14 | "strconv"
15 | "syscall"
16 | "time"
17 |
18 | "github.com/kirides/go-d3d/d3d11"
19 | "github.com/kirides/go-d3d/examples/framelimiter"
20 | "github.com/kirides/go-d3d/outputduplication"
21 | "github.com/kirides/go-d3d/win"
22 |
23 | "github.com/kbinani/screenshot"
24 | "github.com/mattn/go-mjpeg"
25 | )
26 |
27 | func main() {
28 | n := screenshot.NumActiveDisplays()
29 | ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
30 | defer cancel()
31 | http.HandleFunc("/watch", func(w http.ResponseWriter, r *http.Request) {
32 | screen := r.URL.Query().Get("screen")
33 | if screen == "" {
34 | screen = "0"
35 | }
36 | screenNo, err := strconv.Atoi(screen)
37 | if err != nil {
38 | w.WriteHeader(500)
39 | return
40 | }
41 | if screenNo >= n || screenNo < 0 {
42 | screenNo = 0
43 | }
44 |
45 | w.Header().Set("Content-Type", "text/html")
46 | w.Write([]byte(`
47 |
48 |
49 |
50 | Screen ` + strconv.Itoa(screenNo) + `
51 |
52 |
53 |
54 | `))
55 | })
56 |
57 | framerate := 25
58 | for i := 0; i < screenshot.NumActiveDisplays(); i++ {
59 | fmt.Fprintf(os.Stderr, "Registering stream %d\n", i)
60 | stream := mjpeg.NewStream()
61 | defer stream.Close()
62 | go streamDisplayDXGI(ctx, i, framerate, stream)
63 | http.HandleFunc(fmt.Sprintf("/mjpeg%d", i), stream.ServeHTTP)
64 | }
65 | go func() {
66 | http.ListenAndServe("127.0.0.1:8023", nil)
67 |
68 | }()
69 | <-ctx.Done()
70 | <-time.After(time.Second)
71 | }
72 |
73 | // Capture using IDXGIOutputDuplication
74 | //
75 | // https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nn-dxgi1_2-idxgioutputduplication
76 | func streamDisplayDXGI(ctx context.Context, n int, framerate int, out *mjpeg.Stream) {
77 | max := screenshot.NumActiveDisplays()
78 | if n >= max {
79 | fmt.Printf("Not enough displays\n")
80 | return
81 | }
82 |
83 | // Keep this thread, so windows/d3d11/dxgi can use their threadlocal caches, if any
84 | runtime.LockOSThread()
85 | defer runtime.UnlockOSThread()
86 |
87 | // Make thread PerMonitorV2 Dpi aware if supported on OS
88 | // allows to let windows handle BGRA -> RGBA conversion and possibly more things
89 | if win.IsValidDpiAwarenessContext(win.DpiAwarenessContextPerMonitorAwareV2) {
90 | _, err := win.SetThreadDpiAwarenessContext(win.DpiAwarenessContextPerMonitorAwareV2)
91 | if err != nil {
92 | fmt.Printf("Could not set thread DPI awareness to PerMonitorAwareV2. %v\n", err)
93 | } else {
94 | fmt.Printf("Enabled PerMonitorAwareV2 DPI awareness.\n")
95 | }
96 | }
97 |
98 | // Setup D3D11 stuff
99 | device, deviceCtx, err := d3d11.NewD3D11Device()
100 | if err != nil {
101 | fmt.Printf("Could not create D3D11 Device. %v\n", err)
102 | return
103 | }
104 | defer device.Release()
105 | defer deviceCtx.Release()
106 |
107 | var ddup *outputduplication.OutputDuplicator
108 | defer func() {
109 | if ddup != nil {
110 | ddup.Release()
111 | ddup = nil
112 | }
113 | }()
114 |
115 | buf := &bufferFlusher{Buffer: bytes.Buffer{}}
116 | opts := jpegQuality(50)
117 | limiter := framelimiter.New(framerate)
118 |
119 | lastBounds := image.Rectangle{}
120 | var imgBuf *image.RGBA
121 | var lastFrameWithoutCursor *image.RGBA
122 |
123 | drawCursor := true
124 | for {
125 | select {
126 | case <-ctx.Done():
127 | return
128 | default:
129 | limiter.Wait()
130 | }
131 | // create output duplication if doesn't exist yet (maybe due to resolution change)
132 | if ddup == nil {
133 | ddup, err = outputduplication.NewIDXGIOutputDuplication(device, deviceCtx, uint(n))
134 | if err != nil {
135 | fmt.Printf("err: %v\n", err)
136 | continue
137 | }
138 | ddup.UpdatePointerInfo = drawCursor
139 | bounds, err := ddup.GetBounds()
140 | if err != nil {
141 | return
142 | }
143 | if bounds != lastBounds {
144 | lastBounds = bounds
145 | imgBuf = image.NewRGBA(lastBounds)
146 | lastFrameWithoutCursor = image.NewRGBA(lastBounds)
147 | }
148 | }
149 |
150 | // Grab an image.RGBA from the current output presenter
151 | currentImage := imgBuf
152 | err = ddup.GetImage(currentImage, 999)
153 | if err != nil {
154 | if errors.Is(err, outputduplication.ErrNoImageYet) {
155 | // don't update
156 | copy(imgBuf.Pix, lastFrameWithoutCursor.Pix)
157 | } else {
158 | fmt.Printf("Err ddup.GetImage: %v\n", err)
159 | // Retry with new ddup, can occur when changing resolution
160 | ddup.Release()
161 | ddup = nil
162 | continue
163 | }
164 | } else {
165 | copy(lastFrameWithoutCursor.Pix, imgBuf.Pix)
166 | }
167 | if drawCursor {
168 | ddup.DrawCursor(currentImage)
169 | }
170 |
171 | buf.Reset()
172 | encodeJpeg(buf, imgBuf, opts)
173 | out.Update(buf.Bytes())
174 | }
175 | }
176 |
177 | // Workaround for jpeg.Encode(), which requires a Flush()
178 | // method to not call `bufio.NewWriter`
179 | type bufferFlusher struct {
180 | bytes.Buffer
181 | }
182 |
183 | func (*bufferFlusher) Flush() error { return nil }
184 |
--------------------------------------------------------------------------------
/examples/recording/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net/http"
7 |
8 | "os"
9 | "os/signal"
10 | "syscall"
11 | "time"
12 |
13 | "github.com/kbinani/screenshot"
14 | "github.com/mattn/go-mjpeg"
15 | )
16 |
17 | func main() {
18 | ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
19 | defer cancel()
20 |
21 | framerate := 25
22 | for i := 0; i < screenshot.NumActiveDisplays(); i++ {
23 | fmt.Fprintf(os.Stderr, "Registering stream %d\n", i)
24 | stream := mjpeg.NewStream()
25 | defer stream.Close()
26 | go captureScreenTranscode(ctx, i, framerate)
27 | http.HandleFunc(fmt.Sprintf("/mjpeg%d", i), stream.ServeHTTP)
28 | }
29 | <-ctx.Done()
30 | <-time.After(time.Second)
31 | }
32 |
--------------------------------------------------------------------------------
/examples/recording/transcode.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "image"
8 | "io"
9 | "os/exec"
10 | "runtime"
11 | "time"
12 |
13 | "github.com/kbinani/screenshot"
14 | "github.com/kirides/go-d3d/d3d11"
15 | "github.com/kirides/go-d3d/examples/framelimiter"
16 | "github.com/kirides/go-d3d/outputduplication"
17 | "github.com/kirides/go-d3d/win"
18 | )
19 |
20 | func captureScreenTranscode(ctx context.Context, n int, framerate int) {
21 | max := screenshot.NumActiveDisplays()
22 | if n >= max {
23 | fmt.Printf("Not enough displays\n")
24 | return
25 | }
26 |
27 | // Keep this thread, so windows/d3d11/dxgi can use their threadlocal caches, if any
28 | runtime.LockOSThread()
29 |
30 | // Make thread PerMonitorV2 Dpi aware if supported on OS
31 | // allows to let windows handle BGRA -> RGBA conversion and possibly more things
32 | if win.IsValidDpiAwarenessContext(win.DpiAwarenessContextPerMonitorAwareV2) {
33 | _, err := win.SetThreadDpiAwarenessContext(win.DpiAwarenessContextPerMonitorAwareV2)
34 | if err != nil {
35 | fmt.Printf("Could not set thread DPI awareness to PerMonitorAwareV2. %v\n", err)
36 | } else {
37 | fmt.Printf("Enabled PerMonitorAwareV2 DPI awareness.\n")
38 | }
39 | }
40 |
41 | // Setup D3D11 stuff
42 | device, deviceCtx, err := d3d11.NewD3D11Device()
43 | if err != nil {
44 | fmt.Printf("Could not create D3D11 Device. %v\n", err)
45 | return
46 | }
47 | defer device.Release()
48 | defer deviceCtx.Release()
49 |
50 | ddup, err := outputduplication.NewIDXGIOutputDuplication(device, deviceCtx, uint(n))
51 | if err != nil {
52 | fmt.Printf("Err NewIDXGIOutputDuplication: %v\n", err)
53 | return
54 | }
55 | defer ddup.Release()
56 |
57 | screenBounds, err := ddup.GetBounds()
58 | if err != nil {
59 | fmt.Printf("Unable to obtain output bounds: %v\n", err)
60 | return
61 | }
62 | transcoder := newVideotranscoder(fmt.Sprintf("screen_%d.mp4", n), screenBounds.Dx(), screenBounds.Dy(), float32(framerate))
63 |
64 | limiter := framelimiter.New(framerate)
65 |
66 | // Create image that can contain the wanted output (desktop)
67 | imgBuf := image.NewRGBA(screenBounds)
68 |
69 | defer transcoder.Close()
70 | t1 := time.Now()
71 | numFrames := 0
72 | for {
73 | if time.Since(t1).Seconds() >= 1 {
74 | fmt.Printf("%d: written %d frames in 1s\n", n, numFrames)
75 | t1 = time.Now()
76 | numFrames = 0
77 | }
78 | select {
79 | case <-ctx.Done():
80 | return
81 | default:
82 | limiter.Wait()
83 | }
84 | // Grab an image.RGBA from the current output presenter
85 | err = ddup.GetImage(imgBuf, 0)
86 | if err != nil && !errors.Is(err, outputduplication.ErrNoImageYet) {
87 | fmt.Printf("Err ddup.GetImage: %v\n", err)
88 | return
89 | }
90 |
91 | numFrames++
92 |
93 | n, err := transcoder.Write(imgBuf.Pix)
94 | if err != nil || n != len(imgBuf.Pix) {
95 | fmt.Printf("Failed to write image: %v\n", err)
96 | return
97 | }
98 | }
99 | }
100 |
101 | type videotranscoder struct {
102 | cmd *exec.Cmd
103 |
104 | in io.WriteCloser
105 | }
106 |
107 | func newVideotranscoder(filePath string, width, height int, framerate float32) *videotranscoder {
108 | cmd := exec.Command("ffmpeg",
109 | "-y",
110 | "-vsync", "0",
111 | "-f", "rawvideo",
112 | "-video_size", fmt.Sprintf("%dx%d", width, height),
113 | "-pixel_format", "rgba",
114 | "-framerate", fmt.Sprintf("%f", framerate),
115 | "-i", "-",
116 | // "-vf", "scale=-1:1080",
117 | "-c:v", "libx264", "-preset", "ultrafast",
118 | "-crf", "26",
119 | "-tune", "zerolatency",
120 | filePath,
121 | )
122 |
123 | wc, err := cmd.StdinPipe()
124 | if err != nil {
125 | panic(err)
126 | }
127 | if err := cmd.Start(); err != nil {
128 | panic(err)
129 | }
130 | return &videotranscoder{
131 | cmd: cmd,
132 | in: wc,
133 | }
134 | }
135 | func (v *videotranscoder) Write(buf []byte) (int, error) {
136 | return v.in.Write(buf)
137 | }
138 | func (v *videotranscoder) Close() error {
139 | // v.out.Close()
140 | v.in.Close()
141 | return nil
142 | }
143 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/kirides/go-d3d
2 |
3 | go 1.24
4 |
5 | require (
6 | github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c
7 | github.com/mattn/go-mjpeg v0.0.3
8 | github.com/viam-labs/go-libjpeg v0.3.1
9 | golang.org/x/sys v0.33.0
10 | )
11 |
12 | require (
13 | github.com/gen2brain/shm v0.1.1 // indirect
14 | github.com/godbus/dbus/v5 v5.1.0 // indirect
15 | github.com/jezek/xgb v1.1.1 // indirect
16 | github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
17 | )
18 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/gen2brain/shm v0.1.1 h1:1cTVA5qcsUFixnDHl14TmRoxgfWEEZlTezpUj1vm5uQ=
2 | github.com/gen2brain/shm v0.1.1/go.mod h1:UgIcVtvmOu+aCJpqJX7GOtiN7X2ct+TKLg4RTxwPIUA=
3 | github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
4 | github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
5 | github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
6 | github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
7 | github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c h1:1IlzDla/ZATV/FsRn1ETf7ir91PHS2mrd4VMunEtd9k=
8 | github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c/go.mod h1:Pmpz2BLf55auQZ67u3rvyI2vAQvNetkK/4zYUmpauZQ=
9 | github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
10 | github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
11 | github.com/mattn/go-mjpeg v0.0.3 h1:0G/+KddrbI5Hnq83B11O1O4vP7Q6L9MsBu6aW71jhUM=
12 | github.com/mattn/go-mjpeg v0.0.3/go.mod h1:65z7Cj+u5y5K3B8Sy5NtrJFTWAhguGHs9FEkADdx6kE=
13 | github.com/viam-labs/go-libjpeg v0.3.1 h1:J/byavXHFqRI1PFPrnPbP+wFCr1y+Cn1CwKXrORCPD0=
14 | github.com/viam-labs/go-libjpeg v0.3.1/go.mod h1:b0ISpf9lJv9MO1h1gXAmSA/osG19cKGYjfYc6aeEjqs=
15 | golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
16 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
17 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
18 |
--------------------------------------------------------------------------------
/hresult.go:
--------------------------------------------------------------------------------
1 | package d3d
2 |
3 | /*
4 | Contains common D3D11 / DXGI errorcodes and allows to use them as Go errors
5 | */
6 |
7 | //go:generate stringer -type=HRESULT -output=hresult_string.go
8 |
9 | import (
10 | "strconv"
11 | "strings"
12 | )
13 |
14 | type HRESULT uint32
15 |
16 | func (hr HRESULT) Failed() bool {
17 | return int32(hr) < 0
18 | }
19 |
20 | const (
21 | S_OK HRESULT = 0x0
22 | E_INVALIDARG HRESULT = 0x80070057
23 | DXGI_STATUS_OCCLUDED HRESULT = 0x087A0001
24 | DXGI_STATUS_CLIPPED HRESULT = 0x087A0002
25 | DXGI_STATUS_NO_REDIRECTION HRESULT = 0x087A0004
26 | DXGI_STATUS_NO_DESKTOP_ACCESS HRESULT = 0x087A0005
27 | DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE HRESULT = 0x087A0006
28 | DXGI_STATUS_MODE_CHANGED HRESULT = 0x087A0007
29 | DXGI_STATUS_MODE_CHANGE_IN_PROGRESS HRESULT = 0x087A0008
30 | DXGI_ERROR_INVALID_CALL HRESULT = 0x887A0001
31 | DXGI_ERROR_NOT_FOUND HRESULT = 0x887A0002
32 | DXGI_ERROR_MORE_DATA HRESULT = 0x887A0003
33 | DXGI_ERROR_UNSUPPORTED HRESULT = 0x887A0004
34 | DXGI_ERROR_DEVICE_REMOVED HRESULT = 0x887A0005
35 | DXGI_ERROR_DEVICE_HUNG HRESULT = 0x887A0006
36 | DXGI_ERROR_DEVICE_RESET HRESULT = 0x887A0007
37 | DXGI_ERROR_WAS_STILL_DRAWING HRESULT = 0x887A000A
38 | DXGI_ERROR_FRAME_STATISTICS_DISJOINT HRESULT = 0x887A000B
39 | DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE HRESULT = 0x887A000C
40 | DXGI_ERROR_DRIVER_INTERNAL_ERROR HRESULT = 0x887A0020
41 | DXGI_ERROR_NONEXCLUSIVE HRESULT = 0x887A0021
42 | DXGI_ERROR_NOT_CURRENTLY_AVAILABLE HRESULT = 0x887A0022
43 | DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED HRESULT = 0x887A0023
44 | DXGI_ERROR_REMOTE_OUTOFMEMORY HRESULT = 0x887A0024
45 | DXGI_ERROR_ACCESS_LOST HRESULT = 0x887A0026
46 | DXGI_ERROR_WAIT_TIMEOUT HRESULT = 0x887A0027
47 | DXGI_ERROR_SESSION_DISCONNECTED HRESULT = 0x887A0028
48 | DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE HRESULT = 0x887A0029
49 | DXGI_ERROR_CANNOT_PROTECT_CONTENT HRESULT = 0x887A002A
50 | DXGI_ERROR_ACCESS_DENIED HRESULT = 0x887A002B
51 | DXGI_ERROR_NAME_ALREADY_EXISTS HRESULT = 0x887A002C
52 | DXGI_ERROR_SDK_COMPONENT_MISSING HRESULT = 0x887A002D
53 | DXGI_ERROR_NOT_CURRENT HRESULT = 0x887A002E
54 | DXGI_ERROR_HW_PROTECTION_OUTOFMEMORY HRESULT = 0x887A0030
55 | DXGI_ERROR_DYNAMIC_CODE_POLICY_VIOLATION HRESULT = 0x887A0031
56 | DXGI_ERROR_NON_COMPOSITED_UI HRESULT = 0x887A0032
57 | DXGI_STATUS_UNOCCLUDED HRESULT = 0x087A0009
58 | DXGI_STATUS_DDA_WAS_STILL_DRAWING HRESULT = 0x087A000A
59 | DXGI_ERROR_MODE_CHANGE_IN_PROGRESS HRESULT = 0x887A0025
60 | DXGI_STATUS_PRESENT_REQUIRED HRESULT = 0x087A002F
61 | DXGI_ERROR_CACHE_CORRUPT HRESULT = 0x887A0033
62 | DXGI_ERROR_CACHE_FULL HRESULT = 0x887A0034
63 | DXGI_ERROR_CACHE_HASH_COLLISION HRESULT = 0x887A0035
64 | DXGI_ERROR_ALREADY_EXISTS HRESULT = 0x887A0036
65 | DXGI_DDI_ERR_WASSTILLDRAWING HRESULT = 0x887B0001
66 | DXGI_DDI_ERR_UNSUPPORTED HRESULT = 0x887B0002
67 | DXGI_DDI_ERR_NONEXCLUSIVE HRESULT = 0x887B0003
68 | )
69 |
70 | func (e HRESULT) Error() string {
71 | str := e.String()
72 |
73 | if strings.HasSuffix(str, ")") {
74 | // Workaround: return just the hex code as error if no name found
75 | return "0x" + strconv.FormatUint(uint64(e), 16)
76 | }
77 | // return name and hex value
78 | return str + " (0x" + strconv.FormatUint(uint64(e), 16) + ")"
79 | }
80 |
--------------------------------------------------------------------------------
/hresult_string.go:
--------------------------------------------------------------------------------
1 | // Code generated by "stringer -type=HRESULT -output=hresult_string.go"; DO NOT EDIT.
2 |
3 | package d3d
4 |
5 | import "strconv"
6 |
7 | func _() {
8 | // An "invalid array index" compiler error signifies that the constant values have changed.
9 | // Re-run the stringer command to generate them again.
10 | var x [1]struct{}
11 | _ = x[S_OK-0]
12 | _ = x[E_INVALIDARG-2147942487]
13 | _ = x[DXGI_STATUS_OCCLUDED-142213121]
14 | _ = x[DXGI_STATUS_CLIPPED-142213122]
15 | _ = x[DXGI_STATUS_NO_REDIRECTION-142213124]
16 | _ = x[DXGI_STATUS_NO_DESKTOP_ACCESS-142213125]
17 | _ = x[DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE-142213126]
18 | _ = x[DXGI_STATUS_MODE_CHANGED-142213127]
19 | _ = x[DXGI_STATUS_MODE_CHANGE_IN_PROGRESS-142213128]
20 | _ = x[DXGI_ERROR_INVALID_CALL-2289696769]
21 | _ = x[DXGI_ERROR_NOT_FOUND-2289696770]
22 | _ = x[DXGI_ERROR_MORE_DATA-2289696771]
23 | _ = x[DXGI_ERROR_UNSUPPORTED-2289696772]
24 | _ = x[DXGI_ERROR_DEVICE_REMOVED-2289696773]
25 | _ = x[DXGI_ERROR_DEVICE_HUNG-2289696774]
26 | _ = x[DXGI_ERROR_DEVICE_RESET-2289696775]
27 | _ = x[DXGI_ERROR_WAS_STILL_DRAWING-2289696778]
28 | _ = x[DXGI_ERROR_FRAME_STATISTICS_DISJOINT-2289696779]
29 | _ = x[DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE-2289696780]
30 | _ = x[DXGI_ERROR_DRIVER_INTERNAL_ERROR-2289696800]
31 | _ = x[DXGI_ERROR_NONEXCLUSIVE-2289696801]
32 | _ = x[DXGI_ERROR_NOT_CURRENTLY_AVAILABLE-2289696802]
33 | _ = x[DXGI_ERROR_REMOTE_CLIENT_DISCONNECTED-2289696803]
34 | _ = x[DXGI_ERROR_REMOTE_OUTOFMEMORY-2289696804]
35 | _ = x[DXGI_ERROR_ACCESS_LOST-2289696806]
36 | _ = x[DXGI_ERROR_WAIT_TIMEOUT-2289696807]
37 | _ = x[DXGI_ERROR_SESSION_DISCONNECTED-2289696808]
38 | _ = x[DXGI_ERROR_RESTRICT_TO_OUTPUT_STALE-2289696809]
39 | _ = x[DXGI_ERROR_CANNOT_PROTECT_CONTENT-2289696810]
40 | _ = x[DXGI_ERROR_ACCESS_DENIED-2289696811]
41 | _ = x[DXGI_ERROR_NAME_ALREADY_EXISTS-2289696812]
42 | _ = x[DXGI_ERROR_SDK_COMPONENT_MISSING-2289696813]
43 | _ = x[DXGI_ERROR_NOT_CURRENT-2289696814]
44 | _ = x[DXGI_ERROR_HW_PROTECTION_OUTOFMEMORY-2289696816]
45 | _ = x[DXGI_ERROR_DYNAMIC_CODE_POLICY_VIOLATION-2289696817]
46 | _ = x[DXGI_ERROR_NON_COMPOSITED_UI-2289696818]
47 | _ = x[DXGI_STATUS_UNOCCLUDED-142213129]
48 | _ = x[DXGI_STATUS_DDA_WAS_STILL_DRAWING-142213130]
49 | _ = x[DXGI_ERROR_MODE_CHANGE_IN_PROGRESS-2289696805]
50 | _ = x[DXGI_STATUS_PRESENT_REQUIRED-142213167]
51 | _ = x[DXGI_ERROR_CACHE_CORRUPT-2289696819]
52 | _ = x[DXGI_ERROR_CACHE_FULL-2289696820]
53 | _ = x[DXGI_ERROR_CACHE_HASH_COLLISION-2289696821]
54 | _ = x[DXGI_ERROR_ALREADY_EXISTS-2289696822]
55 | _ = x[DXGI_DDI_ERR_WASSTILLDRAWING-2289762305]
56 | _ = x[DXGI_DDI_ERR_UNSUPPORTED-2289762306]
57 | _ = x[DXGI_DDI_ERR_NONEXCLUSIVE-2289762307]
58 | }
59 |
60 | const (
61 | _HRESULT_name_0 = "S_OK"
62 | _HRESULT_name_1 = "DXGI_STATUS_OCCLUDEDDXGI_STATUS_CLIPPED"
63 | _HRESULT_name_2 = "DXGI_STATUS_NO_REDIRECTIONDXGI_STATUS_NO_DESKTOP_ACCESSDXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USEDXGI_STATUS_MODE_CHANGEDDXGI_STATUS_MODE_CHANGE_IN_PROGRESSDXGI_STATUS_UNOCCLUDEDDXGI_STATUS_DDA_WAS_STILL_DRAWING"
64 | _HRESULT_name_3 = "DXGI_STATUS_PRESENT_REQUIRED"
65 | _HRESULT_name_4 = "E_INVALIDARG"
66 | _HRESULT_name_5 = "DXGI_ERROR_INVALID_CALLDXGI_ERROR_NOT_FOUNDDXGI_ERROR_MORE_DATADXGI_ERROR_UNSUPPORTEDDXGI_ERROR_DEVICE_REMOVEDDXGI_ERROR_DEVICE_HUNGDXGI_ERROR_DEVICE_RESET"
67 | _HRESULT_name_6 = "DXGI_ERROR_WAS_STILL_DRAWINGDXGI_ERROR_FRAME_STATISTICS_DISJOINTDXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE"
68 | _HRESULT_name_7 = "DXGI_ERROR_DRIVER_INTERNAL_ERRORDXGI_ERROR_NONEXCLUSIVEDXGI_ERROR_NOT_CURRENTLY_AVAILABLEDXGI_ERROR_REMOTE_CLIENT_DISCONNECTEDDXGI_ERROR_REMOTE_OUTOFMEMORYDXGI_ERROR_MODE_CHANGE_IN_PROGRESSDXGI_ERROR_ACCESS_LOSTDXGI_ERROR_WAIT_TIMEOUTDXGI_ERROR_SESSION_DISCONNECTEDDXGI_ERROR_RESTRICT_TO_OUTPUT_STALEDXGI_ERROR_CANNOT_PROTECT_CONTENTDXGI_ERROR_ACCESS_DENIEDDXGI_ERROR_NAME_ALREADY_EXISTSDXGI_ERROR_SDK_COMPONENT_MISSINGDXGI_ERROR_NOT_CURRENT"
69 | _HRESULT_name_8 = "DXGI_ERROR_HW_PROTECTION_OUTOFMEMORYDXGI_ERROR_DYNAMIC_CODE_POLICY_VIOLATIONDXGI_ERROR_NON_COMPOSITED_UIDXGI_ERROR_CACHE_CORRUPTDXGI_ERROR_CACHE_FULLDXGI_ERROR_CACHE_HASH_COLLISIONDXGI_ERROR_ALREADY_EXISTS"
70 | _HRESULT_name_9 = "DXGI_DDI_ERR_WASSTILLDRAWINGDXGI_DDI_ERR_UNSUPPORTEDDXGI_DDI_ERR_NONEXCLUSIVE"
71 | )
72 |
73 | var (
74 | _HRESULT_index_1 = [...]uint8{0, 20, 39}
75 | _HRESULT_index_2 = [...]uint8{0, 26, 55, 95, 119, 154, 176, 209}
76 | _HRESULT_index_5 = [...]uint8{0, 23, 43, 63, 85, 110, 132, 155}
77 | _HRESULT_index_6 = [...]uint8{0, 28, 64, 103}
78 | _HRESULT_index_7 = [...]uint16{0, 32, 55, 89, 126, 155, 189, 211, 234, 265, 300, 333, 357, 387, 419, 441}
79 | _HRESULT_index_8 = [...]uint8{0, 36, 76, 104, 128, 149, 180, 205}
80 | _HRESULT_index_9 = [...]uint8{0, 28, 52, 77}
81 | )
82 |
83 | func (i HRESULT) String() string {
84 | switch {
85 | case i == 0:
86 | return _HRESULT_name_0
87 | case 142213121 <= i && i <= 142213122:
88 | i -= 142213121
89 | return _HRESULT_name_1[_HRESULT_index_1[i]:_HRESULT_index_1[i+1]]
90 | case 142213124 <= i && i <= 142213130:
91 | i -= 142213124
92 | return _HRESULT_name_2[_HRESULT_index_2[i]:_HRESULT_index_2[i+1]]
93 | case i == 142213167:
94 | return _HRESULT_name_3
95 | case i == 2147942487:
96 | return _HRESULT_name_4
97 | case 2289696769 <= i && i <= 2289696775:
98 | i -= 2289696769
99 | return _HRESULT_name_5[_HRESULT_index_5[i]:_HRESULT_index_5[i+1]]
100 | case 2289696778 <= i && i <= 2289696780:
101 | i -= 2289696778
102 | return _HRESULT_name_6[_HRESULT_index_6[i]:_HRESULT_index_6[i+1]]
103 | case 2289696800 <= i && i <= 2289696814:
104 | i -= 2289696800
105 | return _HRESULT_name_7[_HRESULT_index_7[i]:_HRESULT_index_7[i+1]]
106 | case 2289696816 <= i && i <= 2289696822:
107 | i -= 2289696816
108 | return _HRESULT_name_8[_HRESULT_index_8[i]:_HRESULT_index_8[i+1]]
109 | case 2289762305 <= i && i <= 2289762307:
110 | i -= 2289762305
111 | return _HRESULT_name_9[_HRESULT_index_9[i]:_HRESULT_index_9[i+1]]
112 | default:
113 | return "HRESULT(" + strconv.FormatInt(int64(i), 10) + ")"
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/outputduplication/output_duplication.go:
--------------------------------------------------------------------------------
1 | package outputduplication
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "image"
7 |
8 | "unsafe"
9 |
10 | "github.com/kirides/go-d3d"
11 | "github.com/kirides/go-d3d/d3d11"
12 | "github.com/kirides/go-d3d/dxgi"
13 | "github.com/kirides/go-d3d/outputduplication/swizzle"
14 | )
15 |
16 | type PointerInfo struct {
17 | pos dxgi.POINT
18 |
19 | size dxgi.POINT
20 | shapeInBuffer []byte
21 | shapeOutBuffer *image.RGBA
22 | visible bool
23 | }
24 |
25 | type OutputDuplicator struct {
26 | device *d3d11.ID3D11Device
27 | deviceCtx *d3d11.ID3D11DeviceContext
28 | outputDuplication *dxgi.IDXGIOutputDuplication
29 | dxgiOutput *dxgi.IDXGIOutput5
30 |
31 | stagedTex *d3d11.ID3D11Texture2D
32 | surface *dxgi.IDXGISurface
33 | mappedRect dxgi.DXGI_MAPPED_RECT
34 | size dxgi.POINT
35 |
36 | pointerInfo PointerInfo
37 | // Always draw pointer onto the final image when calling GetImage
38 | DrawPointer bool
39 | // Update pointer information when it changes, used with DrawCursor(image)
40 | UpdatePointerInfo bool
41 |
42 | // TODO: handle DPI? Do we need it?
43 | dirtyRects []dxgi.RECT
44 | movedRects []dxgi.DXGI_OUTDUPL_MOVE_RECT
45 | acquiredFrame bool
46 | needsSwizzle bool // in case we use DuplicateOutput1, swizzle is not neccessery
47 | }
48 |
49 | func (dup *OutputDuplicator) initializeStage(texture *d3d11.ID3D11Texture2D) int32 {
50 |
51 | /*
52 | TODO: Only do this on changes!
53 | */
54 | var hr int32
55 | desc := d3d11.D3D11_TEXTURE2D_DESC{}
56 | hr = texture.GetDesc(&desc)
57 | if d3d.HRESULT(hr).Failed() {
58 | return hr
59 | }
60 |
61 | desc.Usage = d3d11.D3D11_USAGE_STAGING
62 | desc.CPUAccessFlags = d3d11.D3D11_CPU_ACCESS_READ
63 | desc.BindFlags = 0
64 | desc.MipLevels = 1
65 | desc.ArraySize = 1
66 | desc.MiscFlags = 0
67 | desc.SampleDesc.Count = 1
68 |
69 | hr = dup.device.CreateTexture2D(&desc, &dup.stagedTex)
70 | if d3d.HRESULT(hr).Failed() {
71 | return hr
72 | }
73 |
74 | hr = dup.stagedTex.QueryInterface(dxgi.IID_IDXGISurface, &dup.surface)
75 | if d3d.HRESULT(hr).Failed() {
76 | return hr
77 | }
78 | dup.size = dxgi.POINT{X: int32(desc.Width), Y: int32(desc.Height)}
79 |
80 | return 0
81 | }
82 |
83 | func (dup *OutputDuplicator) Release() {
84 | dup.ReleaseFrame()
85 | if dup.stagedTex != nil {
86 | dup.stagedTex.Release()
87 | dup.stagedTex = nil
88 | }
89 | if dup.surface != nil {
90 | dup.surface.Release()
91 | dup.surface = nil
92 | }
93 | if dup.outputDuplication != nil {
94 | dup.outputDuplication.Release()
95 | dup.outputDuplication = nil
96 | }
97 | if dup.dxgiOutput != nil {
98 | dup.dxgiOutput.Release()
99 | dup.dxgiOutput = nil
100 | }
101 | }
102 |
103 | var ErrNoImageYet = errors.New("no image yet")
104 |
105 | type unmapFn func() int32
106 |
107 | func (dup *OutputDuplicator) ReleaseFrame() {
108 | if dup.acquiredFrame {
109 | dup.outputDuplication.ReleaseFrame()
110 | dup.acquiredFrame = false
111 | }
112 | }
113 |
114 | // returns DXGI_FORMAT_B8G8R8A8_UNORM data
115 | func (dup *OutputDuplicator) Snapshot(timeoutMs uint) (unmapFn, *dxgi.DXGI_MAPPED_RECT, *dxgi.POINT, error) {
116 | var hr int32
117 | desc := dxgi.DXGI_OUTDUPL_DESC{}
118 | hr = dup.outputDuplication.GetDesc(&desc)
119 | if hr := d3d.HRESULT(hr); hr.Failed() {
120 | return nil, nil, nil, fmt.Errorf("failed to get the description. %w", hr)
121 | }
122 |
123 | if desc.DesktopImageInSystemMemory != 0 {
124 | // TODO: Figure out WHEN exactly this can occur, and if we can make use of it
125 | dup.size = dxgi.POINT{X: int32(desc.ModeDesc.Width), Y: int32(desc.ModeDesc.Height)}
126 | hr = dup.outputDuplication.MapDesktopSurface(&dup.mappedRect)
127 | if hr := d3d.HRESULT(hr); !hr.Failed() {
128 | return dup.outputDuplication.UnMapDesktopSurface, &dup.mappedRect, &dup.size, nil
129 | }
130 | }
131 |
132 | var desktop *dxgi.IDXGIResource
133 | var frameInfo dxgi.DXGI_OUTDUPL_FRAME_INFO
134 |
135 | // Release a possible previous frame
136 | // TODO: Properly use ReleaseFrame...
137 |
138 | dup.ReleaseFrame()
139 | hrF := dup.outputDuplication.AcquireNextFrame(uint32(timeoutMs), &frameInfo, &desktop)
140 | dup.acquiredFrame = true
141 | if hr := d3d.HRESULT(hrF); hr.Failed() {
142 | if hr == d3d.DXGI_ERROR_WAIT_TIMEOUT {
143 | return nil, nil, nil, ErrNoImageYet
144 | }
145 | return nil, nil, nil, fmt.Errorf("failed to AcquireNextFrame. %w", d3d.HRESULT(hrF))
146 | }
147 | // If we do not release the frame ASAP, we only get FPS / 2 frames :/
148 | // Something wrong here?
149 | // defer dup.ReleaseFrame()
150 | defer desktop.Release()
151 |
152 | if dup.UpdatePointerInfo {
153 | if err := dup.updatePointer(&frameInfo); err != nil {
154 | return nil, nil, nil, err
155 | }
156 | }
157 |
158 | if frameInfo.AccumulatedFrames == 0 {
159 | return nil, nil, nil, ErrNoImageYet
160 | }
161 | var desktop2d *d3d11.ID3D11Texture2D
162 | hr = desktop.QueryInterface(d3d11.IID_ID3D11Texture2D, &desktop2d)
163 | if hr := d3d.HRESULT(hr); hr.Failed() {
164 | return nil, nil, nil, fmt.Errorf("failed to QueryInterface(iid_ID3D11Texture2D, ...). %w", hr)
165 | }
166 | defer desktop2d.Release()
167 |
168 | if dup.stagedTex == nil {
169 | hr = dup.initializeStage(desktop2d)
170 | if hr := d3d.HRESULT(hr); hr.Failed() {
171 | return nil, nil, nil, fmt.Errorf("failed to InitializeStage. %w", hr)
172 | }
173 | }
174 |
175 | // NOTE: we could use a single, large []byte buffer and use it as storage for moved rects & dirty rects
176 | if frameInfo.TotalMetadataBufferSize > 0 {
177 | // Handling moved / dirty rects, to reduce GPU<->CPU memory copying
178 | moveRectsRequired := uint32(1)
179 | for {
180 | if len(dup.movedRects) < int(moveRectsRequired) {
181 | dup.movedRects = make([]dxgi.DXGI_OUTDUPL_MOVE_RECT, moveRectsRequired)
182 | }
183 | hr = dup.outputDuplication.GetFrameMoveRects(dup.movedRects, &moveRectsRequired)
184 | if hr := d3d.HRESULT(hr); hr.Failed() {
185 | if hr == d3d.DXGI_ERROR_MORE_DATA {
186 | continue
187 | }
188 | return nil, nil, nil, fmt.Errorf("failed to GetFrameMoveRects. %w", d3d.HRESULT(hr))
189 | }
190 | dup.movedRects = dup.movedRects[:moveRectsRequired]
191 | break
192 | }
193 |
194 | dirtyRectsRequired := uint32(1)
195 | for {
196 | if len(dup.dirtyRects) < int(dirtyRectsRequired) {
197 | dup.dirtyRects = make([]dxgi.RECT, dirtyRectsRequired)
198 | }
199 | hr = dup.outputDuplication.GetFrameDirtyRects(dup.dirtyRects, &dirtyRectsRequired)
200 | if hr := d3d.HRESULT(hr); hr.Failed() {
201 | if hr == d3d.DXGI_ERROR_MORE_DATA {
202 | continue
203 | }
204 | return nil, nil, nil, fmt.Errorf("failed to GetFrameDirtyRects. %w", d3d.HRESULT(hr))
205 | }
206 | dup.dirtyRects = dup.dirtyRects[:dirtyRectsRequired]
207 | break
208 | }
209 |
210 | box := d3d11.D3D11_BOX{
211 | Front: 0,
212 | Back: 1,
213 | }
214 | if len(dup.movedRects) == 0 {
215 | for i := 0; i < len(dup.dirtyRects); i++ {
216 | box.Left = uint32(dup.dirtyRects[i].Left)
217 | box.Top = uint32(dup.dirtyRects[i].Top)
218 | box.Right = uint32(dup.dirtyRects[i].Right)
219 | box.Bottom = uint32(dup.dirtyRects[i].Bottom)
220 |
221 | dup.deviceCtx.CopySubresourceRegion2D(dup.stagedTex, 0, box.Left, box.Top, 0, desktop2d, 0, &box)
222 | }
223 | } else {
224 | // TODO: handle moved rects, then dirty rects
225 | // for now, just update the whole image instead
226 | dup.deviceCtx.CopyResource2D(dup.stagedTex, desktop2d)
227 | }
228 | } else {
229 | // no frame metadata, copy whole image
230 | dup.deviceCtx.CopyResource2D(dup.stagedTex, desktop2d)
231 | if !dup.needsSwizzle {
232 | dup.needsSwizzle = true
233 | }
234 | print("no frame metadata\n")
235 | }
236 |
237 | hr = dup.surface.Map(&dup.mappedRect, dxgi.DXGI_MAP_READ)
238 | if hr := d3d.HRESULT(hr); hr.Failed() {
239 | return nil, nil, nil, fmt.Errorf("failed to surface_.Map(...). %v", hr)
240 | }
241 | return dup.surface.Unmap, &dup.mappedRect, &dup.size, nil
242 | }
243 |
244 | func (dup *OutputDuplicator) DrawCursor(img *image.RGBA) error {
245 | return dup.drawPointer(img)
246 | }
247 |
248 | func (dup *OutputDuplicator) GetImage(img *image.RGBA, timeoutMs uint) error {
249 | unmap, mappedRect, size, err := dup.Snapshot(timeoutMs)
250 | if err != nil {
251 | return err
252 | }
253 | defer unmap()
254 |
255 | // docs are unclear, but pitch is the total width of each row
256 | dataSize := int(mappedRect.Pitch) * int(size.Y)
257 | data := unsafe.Slice((*byte)(unsafe.Pointer(mappedRect.PBits)), dataSize)
258 |
259 | contentWidth := int(size.X) * 4
260 | dataWidth := int(mappedRect.Pitch)
261 |
262 | var imgStart, dataStart, dataEnd int
263 | // copy source bytes into image.RGBA.Pix, skipping padding
264 | for i := 0; i < int(size.Y); i++ {
265 | dataEnd = dataStart + contentWidth
266 | copy(img.Pix[imgStart:], data[dataStart:dataEnd])
267 | imgStart += contentWidth
268 | dataStart += dataWidth
269 | }
270 |
271 | if dup.needsSwizzle {
272 | swizzle.BGRA(img.Pix)
273 | }
274 |
275 | if dup.DrawPointer {
276 | dup.drawPointer(img)
277 | }
278 |
279 | return nil
280 | }
281 |
282 | func (dup *OutputDuplicator) updatePointer(info *dxgi.DXGI_OUTDUPL_FRAME_INFO) error {
283 | if info.LastMouseUpdateTime == 0 {
284 | return nil
285 | }
286 | dup.pointerInfo.visible = info.PointerPosition.Visible != 0
287 | dup.pointerInfo.pos = info.PointerPosition.Position
288 |
289 | if info.PointerShapeBufferSize != 0 {
290 | // new shape
291 | if len(dup.pointerInfo.shapeInBuffer) < int(info.PointerShapeBufferSize) {
292 | dup.pointerInfo.shapeInBuffer = make([]byte, info.PointerShapeBufferSize)
293 | }
294 | var requiredSize uint32
295 | var pointerInfo dxgi.DXGI_OUTDUPL_POINTER_SHAPE_INFO
296 |
297 | hr := dup.outputDuplication.GetFramePointerShape(info.PointerShapeBufferSize,
298 | dup.pointerInfo.shapeInBuffer,
299 | &requiredSize,
300 | &pointerInfo,
301 | )
302 | if hr != 0 {
303 | return fmt.Errorf("unable to obtain frame pointer shape")
304 | }
305 | neededSize := int(pointerInfo.Width) * int(pointerInfo.Height/2) * 4
306 | dup.pointerInfo.shapeOutBuffer = image.NewRGBA(image.Rect(0, 0, int(pointerInfo.Width), int(pointerInfo.Height)))
307 | if len(dup.pointerInfo.shapeOutBuffer.Pix) < int(neededSize) {
308 | dup.pointerInfo.shapeOutBuffer.Pix = make([]byte, neededSize)
309 | }
310 |
311 | switch pointerInfo.Type {
312 | case dxgi.DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
313 | width := int(pointerInfo.Width)
314 | height := int(pointerInfo.Height) / 2 // Corrected height!
315 |
316 | dup.pointerInfo.size = dxgi.POINT{X: int32(width), Y: int32(height)}
317 |
318 | xor_offset := pointerInfo.Pitch * uint32(height)
319 | andMap := dup.pointerInfo.shapeInBuffer
320 | xorMap := dup.pointerInfo.shapeInBuffer[xor_offset:]
321 | out_pixels := dup.pointerInfo.shapeOutBuffer.Pix
322 | widthBytes := (width + 7) / 8
323 |
324 | for j := 0; j < height; j++ {
325 | for i := 0; i < width; i++ {
326 | byteIndex := j*widthBytes + i/8
327 | bitMask := byte(0x80 >> (i % 8))
328 |
329 | andBit := (andMap[byteIndex] & bitMask) != 0
330 | xorBit := (xorMap[byteIndex] & bitMask) != 0
331 |
332 | outIndex := (j*width + i) * 4
333 | // var r, g, b, a byte
334 |
335 | switch {
336 | case !andBit && !xorBit: // Transparent
337 | // r, g, b, a = 0, 0, 0, 0
338 | *(*uint32)(unsafe.Pointer(&out_pixels[outIndex])) = 0x00000000
339 | case !andBit && xorBit: // Inverted (white)
340 | // r, g, b, a = 255, 255, 255, 255
341 | *(*uint32)(unsafe.Pointer(&out_pixels[outIndex])) = 0xFFFFFFFF
342 | case andBit && !xorBit: // Black
343 | // r, g, b, a = 0, 0, 0, 255 // causes a black plane to be rendered alongside the cursors image
344 | // out_pixels[outIndex+0] = 0 // r
345 | // out_pixels[outIndex+1] = 0 // g
346 | // out_pixels[outIndex+2] = 0 // b
347 | // out_pixels[outIndex+3] = 255 // a
348 | *(*uint32)(unsafe.Pointer(&out_pixels[outIndex])) = 0x00000000
349 | case andBit && xorBit: // Inverted (black)
350 | // r, g, b, a = 255, 255, 255, 255
351 | *(*uint32)(unsafe.Pointer(&out_pixels[outIndex])) = 0xFFFFFFFF
352 | }
353 | }
354 | }
355 |
356 | case dxgi.DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
357 | dup.pointerInfo.size = dxgi.POINT{X: int32(pointerInfo.Width), Y: int32(pointerInfo.Height)}
358 |
359 | out, in := dup.pointerInfo.shapeOutBuffer.Pix, dup.pointerInfo.shapeInBuffer
360 | for j := 0; j < int(pointerInfo.Height); j++ {
361 | tout := out[j*int(pointerInfo.Pitch):]
362 | tin := in[j*int(pointerInfo.Pitch):]
363 | copy(tout, tin[:pointerInfo.Pitch])
364 | }
365 | case dxgi.DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
366 | dup.pointerInfo.size = dxgi.POINT{X: int32(pointerInfo.Width), Y: int32(pointerInfo.Height)}
367 |
368 | // TODO: Properly add mask
369 | out, in := dup.pointerInfo.shapeOutBuffer.Pix, dup.pointerInfo.shapeInBuffer
370 | for j := 0; j < int(pointerInfo.Height); j++ {
371 | tout := out[j*int(pointerInfo.Pitch):]
372 | tin := in[j*int(pointerInfo.Pitch):]
373 | copy(tout, tin[:pointerInfo.Pitch])
374 | }
375 | default:
376 | dup.pointerInfo.size = dxgi.POINT{X: 0, Y: 0}
377 | return fmt.Errorf("unsupported type %v", pointerInfo.Type)
378 | }
379 | }
380 | return nil
381 | }
382 |
383 | func (dup *OutputDuplicator) drawPointer(img *image.RGBA) error {
384 | for j := 0; j < int(dup.pointerInfo.size.Y); j++ {
385 | for i := 0; i < int(dup.pointerInfo.size.X); i++ {
386 | col := dup.pointerInfo.shapeOutBuffer.At(i, j)
387 | _, _, _, a := col.RGBA()
388 | if a == 0 {
389 | // just dont draw invisible pixel?
390 | // TODO: correctly apply mask
391 | continue
392 | }
393 |
394 | img.Set(int(dup.pointerInfo.pos.X)+i, int(dup.pointerInfo.pos.Y)+j, col)
395 | }
396 | }
397 | return nil
398 | }
399 |
400 | func (ddup *OutputDuplicator) GetBounds() (image.Rectangle, error) {
401 | desc := dxgi.DXGI_OUTPUT_DESC{}
402 | hr := ddup.dxgiOutput.GetDesc(&desc)
403 | if hr := d3d.HRESULT(hr); hr.Failed() {
404 | return image.Rectangle{}, fmt.Errorf("failed at dxgiOutput.GetDesc. %w", hr)
405 | }
406 |
407 | return image.Rect(int(desc.DesktopCoordinates.Left), int(desc.DesktopCoordinates.Top), int(desc.DesktopCoordinates.Right), int(desc.DesktopCoordinates.Bottom)), nil
408 | }
409 |
410 | // NewIDXGIOutputDuplication creates a new OutputDuplicator
411 | func NewIDXGIOutputDuplication(device *d3d11.ID3D11Device, deviceCtx *d3d11.ID3D11DeviceContext, output uint) (*OutputDuplicator, error) {
412 | // DEBUG
413 |
414 | var d3dDebug *d3d11.ID3D11Debug
415 | hr := device.QueryInterface(d3d11.IID_ID3D11Debug, &d3dDebug)
416 | if hr := d3d.HRESULT(hr); !hr.Failed() {
417 | defer d3dDebug.Release()
418 |
419 | var d3dInfoQueue *d3d11.ID3D11InfoQueue
420 | hr := d3dDebug.QueryInterface(d3d11.IID_ID3D11InfoQueue, &d3dInfoQueue)
421 | if hr := d3d.HRESULT(hr); hr.Failed() {
422 | return nil, fmt.Errorf("failed at device.QueryInterface. %w", hr)
423 | }
424 | defer d3dInfoQueue.Release()
425 | // defer d3dDebug.ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL)
426 | }
427 |
428 | var dxgiDevice1 *dxgi.IDXGIDevice1
429 | hr = device.QueryInterface(dxgi.IID_IDXGIDevice1, &dxgiDevice1)
430 | if hr := d3d.HRESULT(hr); hr.Failed() {
431 | return nil, fmt.Errorf("failed at device.QueryInterface. %w", hr)
432 | }
433 | defer dxgiDevice1.Release()
434 |
435 | var pdxgiAdapter unsafe.Pointer
436 | hr = dxgiDevice1.GetParent(dxgi.IID_IDXGIAdapter1, &pdxgiAdapter)
437 | if hr := d3d.HRESULT(hr); hr.Failed() {
438 | return nil, fmt.Errorf("failed at dxgiDevice1.GetAdapter. %w", hr)
439 | }
440 | dxgiAdapter := (*dxgi.IDXGIAdapter1)(pdxgiAdapter)
441 | defer dxgiAdapter.Release()
442 |
443 | var dxgiOutput *dxgi.IDXGIOutput
444 | // const DXGI_ERROR_NOT_FOUND = 0x887A0002
445 | hr = int32(dxgiAdapter.EnumOutputs(uint32(output), &dxgiOutput))
446 | if hr := d3d.HRESULT(hr); hr.Failed() {
447 | return nil, fmt.Errorf("failed at dxgiAdapter.EnumOutputs. %w", hr)
448 | }
449 | defer dxgiOutput.Release()
450 |
451 | var dxgiOutput5 *dxgi.IDXGIOutput5
452 | hr = dxgiOutput.QueryInterface(dxgi.IID_IDXGIOutput5, &dxgiOutput5)
453 | if hr := d3d.HRESULT(hr); hr.Failed() {
454 | return nil, fmt.Errorf("failed at dxgiOutput.QueryInterface. %w", hr)
455 | }
456 |
457 | var dup *dxgi.IDXGIOutputDuplication
458 | hr = dxgiOutput5.DuplicateOutput1(dxgiDevice1, 0, []dxgi.DXGI_FORMAT{
459 | dxgi.DXGI_FORMAT_R8G8B8A8_UNORM,
460 | // using the former, we don't have to swizzle ourselves
461 | // DXGI_FORMAT_B8G8R8A8_UNORM,
462 | }, &dup)
463 | needsSwizzle := false
464 | if hr := d3d.HRESULT(hr); hr.Failed() {
465 | needsSwizzle = true
466 | // fancy stuff not supported :/
467 | // fmt.Printf("Info: failed to use dxgiOutput5.DuplicateOutput1, falling back to dxgiOutput1.DuplicateOutput. Missing manifest with DPI awareness set to \"PerMonitorV2\"? %v\n", _DXGI_ERROR(hr))
468 | var dxgiOutput1 *dxgi.IDXGIOutput1
469 | hr := dxgiOutput.QueryInterface(dxgi.IID_IDXGIOutput1, &dxgiOutput1)
470 | if hr := d3d.HRESULT(hr); hr.Failed() {
471 | dxgiOutput5.Release()
472 | return nil, fmt.Errorf("failed at dxgiOutput.QueryInterface. %w", hr)
473 | }
474 | defer dxgiOutput1.Release()
475 | hr = dxgiOutput1.DuplicateOutput(dxgiDevice1, &dup)
476 | if hr := d3d.HRESULT(hr); hr.Failed() {
477 | dxgiOutput5.Release()
478 | return nil, fmt.Errorf("failed at dxgiOutput1.DuplicateOutput. %w", hr)
479 | }
480 | }
481 |
482 | return &OutputDuplicator{device: device, deviceCtx: deviceCtx, outputDuplication: dup, needsSwizzle: needsSwizzle, dxgiOutput: dxgiOutput5}, nil
483 | }
484 |
--------------------------------------------------------------------------------
/outputduplication/swizzle/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 The Go Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions are
5 | met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/outputduplication/swizzle/swizzle_amd64.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package swizzle
6 |
7 | // haveSSSE3 returns whether the CPU supports SSSE3 instructions (i.e. PSHUFB).
8 | //
9 | // Note that this is SSSE3, not SSE3.
10 | func haveSSSE3() bool
11 |
12 | var useBGRA16 = haveSSSE3()
13 |
14 | const useBGRA4 = true
15 |
16 | func bgra16(p []byte)
17 | func bgra4(p []byte)
18 |
--------------------------------------------------------------------------------
/outputduplication/swizzle/swizzle_amd64.s:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | #include "textflag.h"
6 |
7 | // func haveSSSE3() bool
8 | TEXT ·haveSSSE3(SB),NOSPLIT,$0
9 | MOVQ $1, AX
10 | CPUID
11 | SHRQ $9, CX
12 | ANDQ $1, CX
13 | MOVB CX, ret+0(FP)
14 | RET
15 |
16 | // func bgra16(p []byte)
17 | TEXT ·bgra16(SB),NOSPLIT,$0-24
18 | MOVQ p+0(FP), SI
19 | MOVQ len+8(FP), DI
20 |
21 | // Sanity check that len is a multiple of 16.
22 | MOVQ DI, AX
23 | ANDQ $15, AX
24 | JNZ done
25 |
26 | // Make the shuffle control mask (16-byte register X0) look like this,
27 | // where the low order byte comes first:
28 | //
29 | // 02 01 00 03 06 05 04 07 0a 09 08 0b 0e 0d 0c 0f
30 | //
31 | // Load the bottom 8 bytes into X0, the top into X1, then interleave them
32 | // into X0.
33 | MOVQ $0x0704050603000102, AX
34 | MOVQ AX, X0
35 | MOVQ $0x0f0c0d0e0b08090a, AX
36 | MOVQ AX, X1
37 | PUNPCKLQDQ X1, X0
38 |
39 | ADDQ SI, DI
40 | loop:
41 | CMPQ SI, DI
42 | JEQ done
43 |
44 | MOVOU (SI), X1
45 | PSHUFB X0, X1
46 | MOVOU X1, (SI)
47 |
48 | ADDQ $16, SI
49 | JMP loop
50 | done:
51 | RET
52 |
53 | // func bgra4(p []byte)
54 | TEXT ·bgra4(SB),NOSPLIT,$0-24
55 | MOVQ p+0(FP), SI
56 | MOVQ len+8(FP), DI
57 |
58 | // Sanity check that len is a multiple of 4.
59 | MOVQ DI, AX
60 | ANDQ $3, AX
61 | JNZ done
62 |
63 | ADDQ SI, DI
64 | loop:
65 | CMPQ SI, DI
66 | JEQ done
67 |
68 | MOVB 0(SI), AX
69 | MOVB 2(SI), BX
70 | MOVB BX, 0(SI)
71 | MOVB AX, 2(SI)
72 |
73 | ADDQ $4, SI
74 | JMP loop
75 | done:
76 | RET
77 |
--------------------------------------------------------------------------------
/outputduplication/swizzle/swizzle_common.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package swizzle provides functions for converting between RGBA pixel
6 | // formats.
7 | package swizzle // import "golang.org/x/exp/shiny/driver/internal/swizzle"
8 |
9 | // BGRA converts a pixel buffer between Go's RGBA and other systems' BGRA byte
10 | // orders.
11 | //
12 | // It panics if the input slice length is not a multiple of 4.
13 | func BGRA(p []byte) {
14 | if len(p)%4 != 0 {
15 | panic("input slice length is not a multiple of 4")
16 | }
17 |
18 | // Use asm code for 16- or 4-byte chunks, if supported.
19 | if useBGRA16 {
20 | n := len(p) &^ (16 - 1)
21 | bgra16(p[:n])
22 | p = p[n:]
23 | } else if useBGRA4 {
24 | bgra4(p)
25 | return
26 | }
27 |
28 | for i := 0; i < len(p); i += 4 {
29 | p[i+0], p[i+2] = p[i+2], p[i+0]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/outputduplication/swizzle/swizzle_other.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // +build !amd64
6 |
7 | package swizzle
8 |
9 | const (
10 | useBGRA16 = false
11 | useBGRA4 = false
12 | )
13 |
14 | func bgra16(p []byte) { panic("unreachable") }
15 | func bgra4(p []byte) { panic("unreachable") }
16 |
--------------------------------------------------------------------------------
/outputduplication/swizzle/swizzle_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package swizzle
6 |
7 | import (
8 | "bytes"
9 | "math/rand"
10 | "testing"
11 | )
12 |
13 | func TestBGRAShortInput(t *testing.T) {
14 | const s = "012.456.89A.CDE.GHI.KLM.O"
15 | testCases := []string{
16 | 0: "012.456.89A.CDE.GHI.KLM.O",
17 | 1: "210.456.89A.CDE.GHI.KLM.O",
18 | 2: "210.654.89A.CDE.GHI.KLM.O",
19 | 3: "210.654.A98.CDE.GHI.KLM.O",
20 | 4: "210.654.A98.EDC.GHI.KLM.O",
21 | 5: "210.654.A98.EDC.IHG.KLM.O",
22 | 6: "210.654.A98.EDC.IHG.MLK.O",
23 | }
24 | for i, want := range testCases {
25 | b := []byte(s)
26 | BGRA(b[:4*i])
27 | got := string(b)
28 | if got != want {
29 | t.Errorf("i=%d: got %q, want %q", i, got, want)
30 | }
31 | changed := got != s
32 | wantChanged := i != 0
33 | if changed != wantChanged {
34 | t.Errorf("i=%d: changed=%t, want %t", i, changed, wantChanged)
35 | }
36 | }
37 | }
38 |
39 | func TestBGRARandomInput(t *testing.T) {
40 | r := rand.New(rand.NewSource(1))
41 | fastBuf := make([]byte, 1024)
42 | slowBuf := make([]byte, 1024)
43 | for i := range fastBuf {
44 | fastBuf[i] = uint8(r.Intn(256))
45 | }
46 | copy(slowBuf, fastBuf)
47 |
48 | for i := 0; i < 100000; i++ {
49 | o := r.Intn(len(fastBuf))
50 | n := r.Intn(len(fastBuf)-o) &^ 0x03
51 | BGRA(fastBuf[o : o+n])
52 | pureGoBGRA(slowBuf[o : o+n])
53 | if bytes.Equal(fastBuf, slowBuf) {
54 | continue
55 | }
56 | for j := range fastBuf {
57 | x := fastBuf[j]
58 | y := slowBuf[j]
59 | if x != y {
60 | t.Fatalf("iter %d: swizzling [%d:%d+%d]: bytes differ at offset %d (aka %d+%d): %#02x vs %#02x",
61 | i, o, o, n, j, o, j-o, x, y)
62 | }
63 | }
64 | }
65 | }
66 |
67 | func pureGoBGRA(p []byte) {
68 | if len(p)%4 != 0 {
69 | return
70 | }
71 | for i := 0; i < len(p); i += 4 {
72 | p[i+0], p[i+2] = p[i+2], p[i+0]
73 | }
74 | }
75 |
76 | func benchmarkBGRA(b *testing.B, f func([]byte)) {
77 | const w, h = 1920, 1080 // 1080p RGBA.
78 | buf := make([]byte, 4*w*h)
79 | b.ResetTimer()
80 | for i := 0; i < b.N; i++ {
81 | f(buf)
82 | }
83 | }
84 |
85 | func BenchmarkBGRA(b *testing.B) { benchmarkBGRA(b, BGRA) }
86 | func BenchmarkPureGoBGRA(b *testing.B) { benchmarkBGRA(b, pureGoBGRA) }
87 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | This code allows to use D3D11 IDXGIOutputDuplication in Go
2 | ## Examples
3 |
4 | - Encoding an mjpeg stream [examples/mjpegstream](./examples/mjpegstream)
5 | - Recording an h264 video using ffmpeg for transcoding [examples/recording](./examples/recording)
6 |
7 | ## Libaries used
8 |
9 | - `golang.org/x/exp/shiny/driver/internal/swizzle` for faster BGRA -> RGBA conversion (see [shiny LICENSE](./outputduplication/swizzle/LICENSE))
10 |
11 | ## app.manifest
12 |
13 | To make use of `IDXGIOutput5::DuplicateOutput1`, an application has to provide support for PerMonitorV2 DPI-Awareness (Windows 10 1703+) This is usually done by providing an `my-executable.exe.manifest` file either next to the executable, or as an embedded resource.
14 |
15 | In the examples there are calls to `IsValidDpiAwarenessContext` and `SetThreadDpiAwarenessContext` which circumvent the requirement.
16 |
--------------------------------------------------------------------------------
/win/syscall_windows.go:
--------------------------------------------------------------------------------
1 | package win
2 |
3 | //go:generate mkwinsyscall -output zsyscall_windows.go syscall_windows.go
4 |
5 | type (
6 | BOOL uint32
7 | BOOLEAN byte
8 | BYTE byte
9 | DWORD uint32
10 | DWORD64 uint64
11 | HANDLE uintptr
12 | HLOCAL uintptr
13 | LARGE_INTEGER int64
14 | LONG int32
15 | LPVOID uintptr
16 | SIZE_T uintptr
17 | UINT uint32
18 | ULONG_PTR uintptr
19 | ULONGLONG uint64
20 | WORD uint16
21 |
22 | HWND uintptr
23 | )
24 |
25 | type BITMAPINFOHEADER struct {
26 | BiSize uint32
27 | BiWidth int32
28 | BiHeight int32
29 | BiPlanes uint16
30 | BiBitCount uint16
31 | BiCompression uint32
32 | BiSizeImage uint32
33 | BiXPelsPerMeter int32
34 | BiYPelsPerMeter int32
35 | BiClrUsed uint32
36 | BiClrImportant uint32
37 | }
38 | type RGBQUAD struct {
39 | RgbBlue byte
40 | RgbGreen byte
41 | RgbRed byte
42 | RgbReserved byte
43 | }
44 |
45 | type BITMAPINFO struct {
46 | BmiHeader BITMAPINFOHEADER
47 | BmiColors *RGBQUAD
48 | }
49 |
50 | const (
51 | OBJ_BITMAP = 7
52 | )
53 |
54 | const (
55 | DpiAwarenessContextUndefined = 0
56 | DpiAwarenessContextUnaware = -1
57 | DpiAwarenessContextSystemAware = -2
58 | DpiAwarenessContextPerMonitorAware = -3
59 | DpiAwarenessContextPerMonitorAwareV2 = -4
60 | DpiAwarenessContextUnawareGdiScaled = -5
61 | )
62 |
63 | //sys SetThreadDpiAwarenessContext(value int32) (n int, err error) = User32.SetThreadDpiAwarenessContext
64 | //sys IsValidDpiAwarenessContext(value int32) (n bool) = User32.IsValidDpiAwarenessContext
65 |
--------------------------------------------------------------------------------
/win/zsyscall_windows.go:
--------------------------------------------------------------------------------
1 | // Code generated by 'go generate'; DO NOT EDIT.
2 |
3 | package win
4 |
5 | import (
6 | "syscall"
7 | "unsafe"
8 |
9 | "golang.org/x/sys/windows"
10 | )
11 |
12 | var _ unsafe.Pointer
13 |
14 | // Do the interface allocations only once for common
15 | // Errno values.
16 | const (
17 | errnoERROR_IO_PENDING = 997
18 | )
19 |
20 | var (
21 | errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
22 | errERROR_EINVAL error = syscall.EINVAL
23 | )
24 |
25 | // errnoErr returns common boxed Errno values, to prevent
26 | // allocations at runtime.
27 | func errnoErr(e syscall.Errno) error {
28 | switch e {
29 | case 0:
30 | return errERROR_EINVAL
31 | case errnoERROR_IO_PENDING:
32 | return errERROR_IO_PENDING
33 | }
34 | // TODO: add more here, after collecting data on the common
35 | // error values see on Windows. (perhaps when running
36 | // all.bat?)
37 | return e
38 | }
39 |
40 | var (
41 | modUser32 = windows.NewLazySystemDLL("User32.dll")
42 |
43 | procIsValidDpiAwarenessContext = modUser32.NewProc("IsValidDpiAwarenessContext")
44 | procSetThreadDpiAwarenessContext = modUser32.NewProc("SetThreadDpiAwarenessContext")
45 | )
46 |
47 | func IsValidDpiAwarenessContext(value int32) (n bool) {
48 | r0, _, _ := syscall.Syscall(procIsValidDpiAwarenessContext.Addr(), 1, uintptr(value), 0, 0)
49 | n = r0 != 0
50 | return
51 | }
52 |
53 | func SetThreadDpiAwarenessContext(value int32) (n int, err error) {
54 | r0, _, e1 := syscall.Syscall(procSetThreadDpiAwarenessContext.Addr(), 1, uintptr(value), 0, 0)
55 | n = int(r0)
56 | if n == 0 {
57 | err = errnoErr(e1)
58 | }
59 | return
60 | }
61 |
--------------------------------------------------------------------------------