mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
Compare commits
106 Commits
983be9727a
...
419d187488
Author | SHA1 | Date | |
---|---|---|---|
|
419d187488 | ||
|
f34c1c081f | ||
|
22fd1f107e | ||
|
b405003a35 | ||
|
6a00b8680a | ||
|
c180c3c9d1 | ||
|
aa6bc1690e | ||
|
b121468991 | ||
|
d37301eeeb | ||
|
e5d10c47e7 | ||
|
6ac1270cb0 | ||
|
ab4a3af1b5 | ||
|
78ba0ded93 | ||
|
216a110761 | ||
|
7ddc349ae6 | ||
|
d0aaa23005 | ||
|
9442b244fc | ||
|
4818768965 | ||
|
83ae577c80 | ||
|
8be3bb41d5 | ||
|
3279688dd6 | ||
|
ecc3f29af1 | ||
|
1c438cb5c8 | ||
|
13c7518400 | ||
|
38d2892906 | ||
|
5942192b66 | ||
|
b0a4b6ebbf | ||
|
657e04d3d1 | ||
|
002e375d64 | ||
|
a612e74031 | ||
|
ac83181403 | ||
|
d2c58dac8c | ||
|
fbd067c96e | ||
|
53de367d47 | ||
|
2c2a1fe859 | ||
|
5bb060b1e9 | ||
|
a108dac797 | ||
|
bfa9435906 | ||
|
db454548be | ||
|
724ce25260 | ||
|
5926b37c98 | ||
|
b3ad97b22b | ||
|
f0ca3f1870 | ||
|
2261cf76de | ||
|
a391da6c77 | ||
|
10d9660125 | ||
|
5d4a68b0ea | ||
|
a41af4528b | ||
|
caab1ee29f | ||
|
aace620b7e | ||
|
42eef43136 | ||
|
c46f62e184 | ||
|
d7df5ebcbd | ||
|
ca9a80600d | ||
|
322ad99568 | ||
|
c3b9afe8c4 | ||
|
1ebfa8b911 | ||
|
c658a25171 | ||
|
903ab6727b | ||
|
35e29a29e7 | ||
|
e567a8c693 | ||
|
0af5b41d48 | ||
|
12876343ff | ||
|
bb799da51f | ||
|
59fb259181 | ||
|
f34932151d | ||
|
f2a18ed6ba | ||
|
9c374a958e | ||
|
359b5abb62 | ||
|
c390f0a9fa | ||
|
13d15b0ed9 | ||
|
97a9ee9601 | ||
|
ec592390e7 | ||
|
9881530b46 | ||
|
483059184f | ||
|
dc6074bec9 | ||
|
dbe06ec468 | ||
|
d706d38ada | ||
|
0b53525808 | ||
|
7dd4aa9150 | ||
|
2faf8a551d | ||
|
75b6e1fa6d | ||
|
f021b5ded8 | ||
|
b2b88f4bdd | ||
|
042fdab3d0 | ||
|
15ccaf1998 | ||
|
15b5c888ca | ||
|
8c0bc0a2e0 | ||
|
ced0c62827 | ||
|
6a8c00e0aa | ||
|
92a257a557 | ||
|
65646df8ed | ||
|
c73221ac16 | ||
|
1b3bfda17b | ||
|
c9eb30d66f | ||
|
55f1a5d32e | ||
|
fc3a6ed373 | ||
|
c3a358b44b | ||
|
d1fd70495b | ||
|
c7eeae7189 | ||
|
4536fadebe | ||
|
280cc1a732 | ||
|
577664c1cb | ||
|
c247da0f05 | ||
|
9003692630 | ||
|
32b5e3edd8 |
17
.github/workflows/test.yml
vendored
17
.github/workflows/test.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
go: ['1.18.x', '1.19.x', '1.20.x', '1.21.x', '1.22.x']
|
||||
go: ['1.19.x', '1.20.x', '1.21.x', '1.22.x']
|
||||
name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
@ -44,9 +44,10 @@ jobs:
|
||||
|
||||
- name: Install wasmbrowsertest
|
||||
run: |
|
||||
go install github.com/agnivade/wasmbrowsertest@ee76d31b7b9b1645576c1f51fec4c09fe6cf1bb3
|
||||
wasmbrowsertest_version=6e494bb3a5ddfe6cccb449250dbdcaa5777b593d
|
||||
go install github.com/agnivade/wasmbrowsertest@${wasmbrowsertest_version}
|
||||
mv $(go env GOPATH)/bin/wasmbrowsertest${{ runner.os == 'Windows' && '.exe' || '' }} $(go env GOPATH)/bin/go_js_wasm_exec${{ runner.os == 'Windows' && '.exe' || '' }}
|
||||
go install github.com/agnivade/wasmbrowsertest/cmd/cleanenv@ee76d31b7b9b1645576c1f51fec4c09fe6cf1bb3
|
||||
go install github.com/agnivade/wasmbrowsertest/cmd/cleanenv@${wasmbrowsertest_version}
|
||||
|
||||
- name: Prepare ebitenmobile test
|
||||
run: |
|
||||
@ -77,9 +78,6 @@ jobs:
|
||||
go list ./... | grep -v -x -F -f .github/workflows/govetblock_windows.txt | xargs go vet
|
||||
|
||||
- name: go vet (vettool)
|
||||
# Stop vettools for old Go versions. Apparently this is an issue in golang.org/x/tools (golang/go#62519)
|
||||
# TODO: Update golang.org/x/tools and remove this restriction.
|
||||
if: ${{ !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}
|
||||
run: |
|
||||
go install ./internal/vettools
|
||||
go vet -vettool=$(which vettools)${{ runner.os == 'Windows' && '.exe' || '' }} -v ./...
|
||||
@ -144,8 +142,6 @@ jobs:
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install ppa-purge
|
||||
sudo ppa-purge -y ppa:ubuntu-toolchain-r/test # Hack for 32bit Linux (#2667)
|
||||
sudo apt-get install gcc-multilib
|
||||
sudo apt-get install libasound2-dev:i386 libgl1-mesa-dev:i386 libxcursor-dev:i386 libxi-dev:i386 libxinerama-dev:i386 libxrandr-dev:i386 libxxf86vm-dev:i386
|
||||
env CGO_ENABLED=1 GOARCH=386 go test -shuffle=on -v -p=1 ./...
|
||||
@ -169,9 +165,10 @@ jobs:
|
||||
env GOARCH=386 EBITENGINE_DIRECTX=version=12 go test -shuffle=on -v ./...
|
||||
|
||||
- name: go test (Wasm)
|
||||
if: runner.os != 'macOS'
|
||||
if: ${{ runner.os != 'macOS' && runner.os != 'Windows' }}
|
||||
run: |
|
||||
# Wasm tests don't work on macOS with the headless mode, and the headless mode doesn't work in GitHub Actions (#2973).
|
||||
# Wasm tests don't work on macOS with the headless mode enabled, but the headless mode cannot be disabled in GitHub Actions (#2972).
|
||||
# Wasm tests don't work on Windows well due to mysterious timeouts (#2982).
|
||||
env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./...
|
||||
|
||||
- name: Install ebitenmobile
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -7,3 +7,8 @@
|
||||
.vscode
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
*.fxc
|
||||
!dummy.fxc
|
||||
*.metallib
|
||||
!dummy.metallib
|
||||
|
129
README.md
129
README.md
@ -61,3 +61,132 @@ For installation on desktops, see [the installation instruction](https://ebiteng
|
||||
Ebitengine is licensed under Apache license version 2.0. See [LICENSE](LICENSE) file.
|
||||
|
||||
[The Ebitengine logo](https://ebitengine.org/images/logo.png) by Hajime Hoshi is licensed under [the Creative Commons Attribution-NoDerivatives 4.0](https://creativecommons.org/licenses/by-nd/4.0/).
|
||||
|
||||
### GLFW
|
||||
|
||||
https://github.com/glfw/glfw
|
||||
|
||||
|
||||
```
|
||||
Copyright (c) 2002-2006 Marcus Geelnard
|
||||
|
||||
Copyright (c) 2006-2019 Camilla Löwy
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would
|
||||
be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not
|
||||
be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
https://cs.opensource.google/go/go
|
||||
|
||||
|
||||
```
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
```
|
||||
|
||||
### go-gl/gl
|
||||
|
||||
https://github.com/go-gl/gl
|
||||
|
||||
|
||||
```
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Eric Woroshow
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
### go-gl/glfw
|
||||
|
||||
https://github.com/go-gl/glfw
|
||||
|
||||
|
||||
```
|
||||
Copyright (c) 2012 The glfw3-go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
```
|
||||
|
@ -31,6 +31,7 @@ import (
|
||||
type Stream struct {
|
||||
orig *mp3.Decoder
|
||||
resampling *convert.Resampling
|
||||
sampleRate int
|
||||
}
|
||||
|
||||
// Read is implementation of io.Reader's Read.
|
||||
@ -57,6 +58,11 @@ func (s *Stream) Length() int64 {
|
||||
return s.orig.Length()
|
||||
}
|
||||
|
||||
// SampleRate returns the sample rate of the decoded stream.
|
||||
func (s *Stream) SampleRate() int {
|
||||
return s.sampleRate
|
||||
}
|
||||
|
||||
// DecodeWithoutResampling decodes an MP3 source and returns a decoded stream.
|
||||
//
|
||||
// DecodeWithoutResampling returns error when decoding fails or IO error happens.
|
||||
@ -73,6 +79,7 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
|
||||
s := &Stream{
|
||||
orig: d,
|
||||
resampling: nil,
|
||||
sampleRate: d.SampleRate(),
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
@ -87,6 +94,9 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
|
||||
//
|
||||
// A Stream doesn't close src even if src implements io.Closer.
|
||||
// Closing the source is src owner's responsibility.
|
||||
//
|
||||
// Resampling can be a very heavy task. Stream has a cache for resampling, but the size is limited.
|
||||
// Do not expect that Stream has a resampling cache even after whole data is played.
|
||||
func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) {
|
||||
d, err := mp3.NewDecoder(src)
|
||||
if err != nil {
|
||||
@ -100,6 +110,7 @@ func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) {
|
||||
s := &Stream{
|
||||
orig: d,
|
||||
resampling: r,
|
||||
sampleRate: sampleRate,
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
@ -27,50 +27,48 @@ import (
|
||||
|
||||
// Stream is a decoded audio stream.
|
||||
type Stream struct {
|
||||
decoded io.ReadSeeker
|
||||
size int64
|
||||
readSeeker io.ReadSeeker
|
||||
length int64
|
||||
sampleRate int
|
||||
}
|
||||
|
||||
// Read is implementation of io.Reader's Read.
|
||||
func (s *Stream) Read(p []byte) (int, error) {
|
||||
return s.decoded.Read(p)
|
||||
return s.readSeeker.Read(p)
|
||||
}
|
||||
|
||||
// Seek is implementation of io.Seeker's Seek.
|
||||
//
|
||||
// Note that Seek can take long since decoding is a relatively heavy task.
|
||||
func (s *Stream) Seek(offset int64, whence int) (int64, error) {
|
||||
return s.decoded.Seek(offset, whence)
|
||||
return s.readSeeker.Seek(offset, whence)
|
||||
}
|
||||
|
||||
// Length returns the size of decoded stream in bytes.
|
||||
//
|
||||
// If the source is not io.Seeker, Length returns 0.
|
||||
func (s *Stream) Length() int64 {
|
||||
return s.size
|
||||
return s.length
|
||||
}
|
||||
|
||||
type decoder interface {
|
||||
Read([]float32) (int, error)
|
||||
SetPosition(int64) error
|
||||
Length() int64
|
||||
Channels() int
|
||||
SampleRate() int
|
||||
// SampleRate returns the sample rate of the decoded stream.
|
||||
func (s *Stream) SampleRate() int {
|
||||
return s.sampleRate
|
||||
}
|
||||
|
||||
type decoded struct {
|
||||
totalBytes int
|
||||
posInBytes int
|
||||
decoder decoder
|
||||
decoderr io.Reader
|
||||
type i16Stream struct {
|
||||
totalBytes int
|
||||
posInBytes int
|
||||
vorbisReader *oggvorbis.Reader
|
||||
i16Reader io.Reader
|
||||
}
|
||||
|
||||
func (d *decoded) Read(b []byte) (int, error) {
|
||||
if d.decoderr == nil {
|
||||
d.decoderr = convert.NewReaderFromFloat32Reader(d.decoder)
|
||||
func (s *i16Stream) Read(b []byte) (int, error) {
|
||||
if s.i16Reader == nil {
|
||||
s.i16Reader = convert.NewReaderFromFloat32Reader(s.vorbisReader)
|
||||
}
|
||||
|
||||
l := d.totalBytes - d.posInBytes
|
||||
l := s.totalBytes - s.posInBytes
|
||||
if l > len(b) {
|
||||
l = len(b)
|
||||
}
|
||||
@ -79,7 +77,7 @@ func (d *decoded) Read(b []byte) (int, error) {
|
||||
}
|
||||
|
||||
retry:
|
||||
n, err := d.decoderr.Read(b[:l])
|
||||
n, err := s.i16Reader.Read(b[:l])
|
||||
if err != nil && err != io.EOF {
|
||||
return 0, err
|
||||
}
|
||||
@ -88,59 +86,63 @@ retry:
|
||||
goto retry
|
||||
}
|
||||
|
||||
d.posInBytes += n
|
||||
if d.posInBytes == d.totalBytes || err == io.EOF {
|
||||
s.posInBytes += n
|
||||
if s.posInBytes == s.totalBytes || err == io.EOF {
|
||||
return n, io.EOF
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *decoded) Seek(offset int64, whence int) (int64, error) {
|
||||
func (s *i16Stream) Seek(offset int64, whence int) (int64, error) {
|
||||
next := int64(0)
|
||||
switch whence {
|
||||
case io.SeekStart:
|
||||
next = offset
|
||||
case io.SeekCurrent:
|
||||
next = int64(d.posInBytes) + offset
|
||||
next = int64(s.posInBytes) + offset
|
||||
case io.SeekEnd:
|
||||
next = int64(d.totalBytes) + offset
|
||||
next = int64(s.totalBytes) + offset
|
||||
}
|
||||
// pos should be always even
|
||||
next = next / 2 * 2
|
||||
d.posInBytes = int(next)
|
||||
if err := d.decoder.SetPosition(next / int64(d.decoder.Channels()) / 2); err != nil {
|
||||
s.posInBytes = int(next)
|
||||
if err := s.vorbisReader.SetPosition(next / int64(s.vorbisReader.Channels()) / 2); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.decoderr = nil
|
||||
s.i16Reader = nil
|
||||
return next, nil
|
||||
}
|
||||
|
||||
func (d *decoded) Length() int64 {
|
||||
return int64(d.totalBytes)
|
||||
func (s *i16Stream) Length() int64 {
|
||||
return int64(s.totalBytes)
|
||||
}
|
||||
|
||||
// decode accepts an ogg stream and returns a decorded stream.
|
||||
func decode(in io.Reader) (*decoded, int, int, error) {
|
||||
func decode(in io.Reader) (*i16Stream, int, int, error) {
|
||||
r, err := oggvorbis.NewReader(in)
|
||||
if err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
d := &decoded{
|
||||
if r.Channels() != 1 && r.Channels() != 2 {
|
||||
return nil, 0, 0, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", r.Channels())
|
||||
}
|
||||
|
||||
s := &i16Stream{
|
||||
// TODO: r.Length() returns 0 when the format is unknown.
|
||||
// Should we check that?
|
||||
totalBytes: int(r.Length()) * r.Channels() * 2, // 2 means 16bit per sample.
|
||||
posInBytes: 0,
|
||||
decoder: r,
|
||||
totalBytes: int(r.Length()) * r.Channels() * 2, // 2 means 16bit per sample.
|
||||
posInBytes: 0,
|
||||
vorbisReader: r,
|
||||
}
|
||||
if _, ok := in.(io.Seeker); ok {
|
||||
if _, err := d.Read(make([]byte, 65536)); err != nil && err != io.EOF {
|
||||
if _, err := s.Read(make([]byte, 65536)); err != nil && err != io.EOF {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
if _, err := d.Seek(0, io.SeekStart); err != nil {
|
||||
if _, err := s.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, 0, 0, err
|
||||
}
|
||||
}
|
||||
return d, r.Channels(), r.SampleRate(), nil
|
||||
return s, r.Channels(), r.SampleRate(), nil
|
||||
}
|
||||
|
||||
// DecodeWithoutResampling decodes Ogg/Vorbis data to playable stream.
|
||||
@ -152,22 +154,22 @@ func decode(in io.Reader) (*decoded, int, int, error) {
|
||||
// A Stream doesn't close src even if src implements io.Closer.
|
||||
// Closing the source is src owner's responsibility.
|
||||
func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
|
||||
decoded, channelCount, _, err := decode(src)
|
||||
i16Stream, channelCount, sampleRate, err := decode(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if channelCount != 1 && channelCount != 2 {
|
||||
return nil, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", channelCount)
|
||||
}
|
||||
var s io.ReadSeeker = decoded
|
||||
size := decoded.Length()
|
||||
|
||||
var s io.ReadSeeker = i16Stream
|
||||
length := i16Stream.Length()
|
||||
if channelCount == 1 {
|
||||
s = convert.NewStereo16(s, true, false)
|
||||
size *= 2
|
||||
length *= 2
|
||||
}
|
||||
|
||||
stream := &Stream{
|
||||
decoded: s,
|
||||
size: size,
|
||||
readSeeker: s,
|
||||
length: length,
|
||||
sampleRate: sampleRate,
|
||||
}
|
||||
return stream, nil
|
||||
}
|
||||
@ -182,26 +184,31 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
|
||||
//
|
||||
// A Stream doesn't close src even if src implements io.Closer.
|
||||
// Closing the source is src owner's responsibility.
|
||||
//
|
||||
// Resampling can be a very heavy task. Stream has a cache for resampling, but the size is limited.
|
||||
// Do not expect that Stream has a resampling cache even after whole data is played.
|
||||
func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) {
|
||||
decoded, channelCount, origSampleRate, err := decode(src)
|
||||
i16Stream, channelCount, origSampleRate, err := decode(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if channelCount != 1 && channelCount != 2 {
|
||||
return nil, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", channelCount)
|
||||
}
|
||||
var s io.ReadSeeker = decoded
|
||||
size := decoded.Length()
|
||||
|
||||
var s io.ReadSeeker = i16Stream
|
||||
length := i16Stream.Length()
|
||||
if channelCount == 1 {
|
||||
s = convert.NewStereo16(s, true, false)
|
||||
size *= 2
|
||||
length *= 2
|
||||
}
|
||||
if origSampleRate != sampleRate {
|
||||
r := convert.NewResampling(s, size, origSampleRate, sampleRate)
|
||||
r := convert.NewResampling(s, length, origSampleRate, sampleRate)
|
||||
s = r
|
||||
size = r.Length()
|
||||
length = r.Length()
|
||||
}
|
||||
stream := &Stream{
|
||||
readSeeker: s,
|
||||
length: length,
|
||||
sampleRate: sampleRate,
|
||||
}
|
||||
stream := &Stream{decoded: s, size: size}
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
|
@ -50,16 +50,16 @@ func TestMono(t *testing.T) {
|
||||
}
|
||||
|
||||
// Stream decoded by audio/vorbis.DecodeWithSampleRate() is always 16bit stereo.
|
||||
got := s.Length()
|
||||
|
||||
// On the other hand, the original vorbis package is monoral.
|
||||
// As Length() represents the number of samples,
|
||||
// this needs to be doubled by 2 (= bytes in 16bits).
|
||||
want := r.Length() * 2 * 2
|
||||
|
||||
if got != want {
|
||||
if got, want := s.Length(), r.Length()*2*2; got != want {
|
||||
t.Errorf("s.Length(): got: %d, want: %d", got, want)
|
||||
}
|
||||
|
||||
if got, want := s.SampleRate(), audioContext.SampleRate(); got != want {
|
||||
t.Errorf("s.SampleRate(): got: %d, want: %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTooShort(t *testing.T) {
|
||||
@ -70,11 +70,13 @@ func TestTooShort(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := s.Length()
|
||||
want := int64(79424)
|
||||
if got != want {
|
||||
if got, want := s.Length(), int64(79424); got != want {
|
||||
t.Errorf("s.Length(): got: %d, want: %d", got, want)
|
||||
}
|
||||
|
||||
if got, want := s.SampleRate(), audioContext.SampleRate(); got != want {
|
||||
t.Errorf("s.SampleRate(): got: %d, want: %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
@ -93,9 +95,11 @@ func TestNonSeeker(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := s.Length()
|
||||
want := int64(0)
|
||||
if got != want {
|
||||
if got, want := s.Length(), int64(0); got != want {
|
||||
t.Errorf("s.Length(): got: %d, want: %d", got, want)
|
||||
}
|
||||
|
||||
if got, want := s.SampleRate(), audioContext.SampleRate(); got != want {
|
||||
t.Errorf("s.SampleRate(): got: %d, want: %d", got, want)
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,9 @@ import (
|
||||
|
||||
// Stream is a decoded audio stream.
|
||||
type Stream struct {
|
||||
inner io.ReadSeeker
|
||||
size int64
|
||||
inner io.ReadSeeker
|
||||
size int64
|
||||
sampleRate int
|
||||
}
|
||||
|
||||
// Read is implementation of io.Reader's Read.
|
||||
@ -49,6 +50,11 @@ func (s *Stream) Length() int64 {
|
||||
return s.size
|
||||
}
|
||||
|
||||
// SampleRate returns the sample rate of the decoded stream.
|
||||
func (s *Stream) SampleRate() int {
|
||||
return s.sampleRate
|
||||
}
|
||||
|
||||
type stream struct {
|
||||
src io.Reader
|
||||
headerSize int64
|
||||
@ -114,7 +120,7 @@ func (s *stream) Seek(offset int64, whence int) (int64, error) {
|
||||
// A Stream doesn't close src even if src implements io.Closer.
|
||||
// Closing the source is src owner's responsibility.
|
||||
func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
|
||||
s, _, err := decode(src)
|
||||
s, err := decode(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -134,37 +140,41 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) {
|
||||
//
|
||||
// A Stream doesn't close src even if src implements io.Closer.
|
||||
// Closing the source is src owner's responsibility.
|
||||
//
|
||||
// Resampling can be a very heavy task. Stream has a cache for resampling, but the size is limited.
|
||||
// Do not expect that Stream has a resampling cache even after whole data is played.
|
||||
func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) {
|
||||
s, origSampleRate, err := decode(src)
|
||||
s, err := decode(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sampleRate == origSampleRate {
|
||||
if sampleRate == s.sampleRate {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
r := convert.NewResampling(s.inner, s.size, origSampleRate, sampleRate)
|
||||
r := convert.NewResampling(s.inner, s.size, s.sampleRate, sampleRate)
|
||||
return &Stream{
|
||||
inner: r,
|
||||
size: r.Length(),
|
||||
inner: r,
|
||||
size: r.Length(),
|
||||
sampleRate: sampleRate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decode(src io.Reader) (*Stream, int, error) {
|
||||
func decode(src io.Reader) (*Stream, error) {
|
||||
buf := make([]byte, 12)
|
||||
n, err := io.ReadFull(src, buf)
|
||||
if n != len(buf) {
|
||||
return nil, 0, fmt.Errorf("wav: invalid header")
|
||||
return nil, fmt.Errorf("wav: invalid header")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
if !bytes.Equal(buf[0:4], []byte("RIFF")) {
|
||||
return nil, 0, fmt.Errorf("wav: invalid header: 'RIFF' not found")
|
||||
return nil, fmt.Errorf("wav: invalid header: 'RIFF' not found")
|
||||
}
|
||||
if !bytes.Equal(buf[8:12], []byte("WAVE")) {
|
||||
return nil, 0, fmt.Errorf("wav: invalid header: 'WAVE' not found")
|
||||
return nil, fmt.Errorf("wav: invalid header: 'WAVE' not found")
|
||||
}
|
||||
|
||||
// Read chunks
|
||||
@ -178,10 +188,10 @@ chunks:
|
||||
buf := make([]byte, 8)
|
||||
n, err := io.ReadFull(src, buf)
|
||||
if n != len(buf) {
|
||||
return nil, 0, fmt.Errorf("wav: invalid header")
|
||||
return nil, fmt.Errorf("wav: invalid header")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
headerSize += 8
|
||||
size := int64(buf[4]) | int64(buf[5])<<8 | int64(buf[6])<<16 | int64(buf[7])<<24
|
||||
@ -189,19 +199,19 @@ chunks:
|
||||
case bytes.Equal(buf[0:4], []byte("fmt ")):
|
||||
// Size of 'fmt' header is usually 16, but can be more than 16.
|
||||
if size < 16 {
|
||||
return nil, 0, fmt.Errorf("wav: invalid header: maybe non-PCM file?")
|
||||
return nil, fmt.Errorf("wav: invalid header: maybe non-PCM file?")
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
n, err := io.ReadFull(src, buf)
|
||||
if n != len(buf) {
|
||||
return nil, 0, fmt.Errorf("wav: invalid header")
|
||||
return nil, fmt.Errorf("wav: invalid header")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
format := int(buf[0]) | int(buf[1])<<8
|
||||
if format != 1 {
|
||||
return nil, 0, fmt.Errorf("wav: format must be linear PCM")
|
||||
return nil, fmt.Errorf("wav: format must be linear PCM")
|
||||
}
|
||||
channelCount := int(buf[2]) | int(buf[3])<<8
|
||||
switch channelCount {
|
||||
@ -210,11 +220,11 @@ chunks:
|
||||
case 2:
|
||||
mono = false
|
||||
default:
|
||||
return nil, 0, fmt.Errorf("wav: number of channels must be 1 or 2 but was %d", channelCount)
|
||||
return nil, fmt.Errorf("wav: number of channels must be 1 or 2 but was %d", channelCount)
|
||||
}
|
||||
bitsPerSample = int(buf[14]) | int(buf[15])<<8
|
||||
if bitsPerSample != 8 && bitsPerSample != 16 {
|
||||
return nil, 0, fmt.Errorf("wav: bits per sample must be 8 or 16 but was %d", bitsPerSample)
|
||||
return nil, fmt.Errorf("wav: bits per sample must be 8 or 16 but was %d", bitsPerSample)
|
||||
}
|
||||
sampleRate = int(buf[4]) | int(buf[5])<<8 | int(buf[6])<<16 | int(buf[7])<<24
|
||||
headerSize += size
|
||||
@ -225,10 +235,10 @@ chunks:
|
||||
buf := make([]byte, size)
|
||||
n, err := io.ReadFull(src, buf)
|
||||
if n != len(buf) {
|
||||
return nil, 0, fmt.Errorf("wav: invalid header")
|
||||
return nil, fmt.Errorf("wav: invalid header")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, err
|
||||
}
|
||||
headerSize += size
|
||||
}
|
||||
@ -249,7 +259,11 @@ chunks:
|
||||
dataSize *= 2
|
||||
}
|
||||
}
|
||||
return &Stream{inner: s, size: dataSize}, sampleRate, nil
|
||||
return &Stream{
|
||||
inner: s,
|
||||
size: dataSize,
|
||||
sampleRate: sampleRate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Decode decodes WAV (RIFF) data to playable stream.
|
||||
|
@ -23,10 +23,10 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
exec "golang.org/x/sys/execabs"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
|
@ -18,13 +18,13 @@ import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
|
||||
// Add a dependency on gomobile in order to get the version via debug.ReadBuildInfo().
|
||||
_ "github.com/ebitengine/gomobile/geom"
|
||||
exec "golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
//go:embed gobind.go
|
||||
|
@ -27,12 +27,12 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
|
||||
exec "golang.org/x/sys/execabs"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
|
@ -72,11 +72,11 @@ type DrawTrianglesOptions struct {
|
||||
|
||||
// FillRule indicates the rule how an overlapped region is rendered.
|
||||
//
|
||||
// The rules NonZero and EvenOdd are useful when you want to render a complex polygon.
|
||||
// The rules FileRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon.
|
||||
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
||||
// See examples/vector for actual usages.
|
||||
//
|
||||
// The default (zero) value is ebiten.FillAll.
|
||||
// The default (zero) value is ebiten.FillRuleFillAll.
|
||||
FillRule ebiten.FillRule
|
||||
|
||||
// AntiAlias indicates whether the rendering uses anti-alias or not.
|
||||
|
32
ebiten_test.go
Normal file
32
ebiten_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ebiten_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
func TestScreenSizeInFullscreen(t *testing.T) {
|
||||
// Just call ScreenSizeInFullscreen. There was a crash bug on browsers (#2975).
|
||||
w, h := ebiten.ScreenSizeInFullscreen()
|
||||
if w <= 0 {
|
||||
t.Errorf("w must be positive but not: %d", w)
|
||||
}
|
||||
if h <= 0 {
|
||||
t.Errorf("h must be positive but not: %d", h)
|
||||
}
|
||||
}
|
@ -40,6 +40,7 @@ func OpenFile(path string) (ReadSeekCloser, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -171,7 +171,7 @@ func NewPlayer(game *Game, audioContext *audio.Context, musicType musicType) (*P
|
||||
|
||||
player.audioPlayer.Play()
|
||||
go func() {
|
||||
s, err := wav.DecodeWithSampleRate(sampleRate, bytes.NewReader(raudio.Jab_wav))
|
||||
s, err := wav.DecodeWithoutResampling(bytes.NewReader(raudio.Jab_wav))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return
|
||||
|
134
examples/mrt/main.go
Normal file
134
examples/mrt/main.go
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2024 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/jpeg"
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
)
|
||||
|
||||
const (
|
||||
dstSize = 128
|
||||
screenWidth = dstSize * 2
|
||||
screenHeight = dstSize * 2
|
||||
)
|
||||
|
||||
var (
|
||||
dsts = [8]*ebiten.Image{
|
||||
/*ebiten.NewImage(dstSize, dstSize),
|
||||
ebiten.NewImage(dstSize, dstSize),
|
||||
ebiten.NewImage(dstSize, dstSize),
|
||||
ebiten.NewImage(dstSize, dstSize),*/
|
||||
ebiten.NewImageWithOptions(image.Rect(0, 0, dstSize, dstSize), &ebiten.NewImageOptions{
|
||||
Unmanaged: true,
|
||||
}),
|
||||
ebiten.NewImageWithOptions(image.Rect(0, 0, dstSize, dstSize), &ebiten.NewImageOptions{
|
||||
Unmanaged: true,
|
||||
}),
|
||||
ebiten.NewImageWithOptions(image.Rect(0, 0, dstSize, dstSize), &ebiten.NewImageOptions{
|
||||
Unmanaged: true,
|
||||
}),
|
||||
ebiten.NewImageWithOptions(image.Rect(0, 0, dstSize, dstSize), &ebiten.NewImageOptions{
|
||||
Unmanaged: true,
|
||||
}),
|
||||
}
|
||||
|
||||
shaderSrc = []byte(
|
||||
`
|
||||
//kage:units pixels
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(dst vec4, src vec2, color vec4) (vec4, vec4, vec4, vec4) {
|
||||
return vec4(1,0,0,1), vec4(0,1,0,1), vec4(0,0,1,1), vec4(1,0,1,1)
|
||||
}
|
||||
`)
|
||||
s *ebiten.Shader
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
s, err = ebiten.NewShader(shaderSrc)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
vertices := []ebiten.Vertex{
|
||||
{
|
||||
DstX: 0,
|
||||
DstY: 0,
|
||||
},
|
||||
{
|
||||
DstX: dstSize,
|
||||
DstY: 0,
|
||||
},
|
||||
{
|
||||
DstX: 0,
|
||||
DstY: dstSize,
|
||||
},
|
||||
{
|
||||
DstX: dstSize,
|
||||
DstY: dstSize,
|
||||
},
|
||||
}
|
||||
indices := []uint16{0, 1, 2, 1, 2, 3}
|
||||
ebiten.DrawTrianglesShaderMRT(dsts, vertices, indices, s, nil)
|
||||
// Dst 0
|
||||
screen.DrawImage(dsts[0], nil)
|
||||
// Dst 1
|
||||
opts := &ebiten.DrawImageOptions{}
|
||||
opts.GeoM.Translate(dstSize, 0)
|
||||
screen.DrawImage(dsts[1], opts)
|
||||
// Dst 2
|
||||
opts.GeoM.Reset()
|
||||
opts.GeoM.Translate(0, dstSize)
|
||||
screen.DrawImage(dsts[2], opts)
|
||||
// Dst 3
|
||||
opts.GeoM.Reset()
|
||||
opts.GeoM.Translate(dstSize, dstSize)
|
||||
screen.DrawImage(dsts[3], opts)
|
||||
|
||||
ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %.2f", ebiten.ActualFPS()))
|
||||
}
|
||||
|
||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
return screenWidth, screenHeight
|
||||
}
|
||||
|
||||
func main() {
|
||||
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||
ebiten.SetVsyncEnabled(false)
|
||||
ebiten.SetWindowTitle("MRT (Ebitengine Demo)")
|
||||
if err := ebiten.RunGameWithOptions(&Game{}, &ebiten.RunGameOptions{
|
||||
GraphicsLibrary: ebiten.GraphicsLibraryDirectX,
|
||||
}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
33
examples/shaderprecomp/defaultshader.go
Normal file
33
examples/shaderprecomp/defaultshader.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2020 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
//kage:unit pixels
|
||||
|
||||
package main
|
||||
|
||||
var Time float
|
||||
var Cursor vec2
|
||||
|
||||
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
pos := (dstPos.xy - imageDstOrigin()) / imageDstSize()
|
||||
pos += Cursor / imageDstSize() / 4
|
||||
clr := 0.0
|
||||
clr += sin(pos.x*cos(Time/15)*80) + cos(pos.y*cos(Time/15)*10)
|
||||
clr += sin(pos.y*sin(Time/10)*40) + cos(pos.x*sin(Time/25)*40)
|
||||
clr += sin(pos.x*sin(Time/5)*10) + sin(pos.y*sin(Time/35)*80)
|
||||
clr *= sin(Time/10) * 0.5
|
||||
return vec4(clr, clr*0.5, sin(clr+Time/3)*0.75, 1)
|
||||
}
|
1
examples/shaderprecomp/fxc/dummy.fxc
Normal file
1
examples/shaderprecomp/fxc/dummy.fxc
Normal file
@ -0,0 +1 @@
|
||||
This is a dummy .fxc file to trick Go's embed package.
|
123
examples/shaderprecomp/fxc/gen.go
Normal file
123
examples/shaderprecomp/fxc/gen.go
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
// This is a program to generate precompiled HLSL blobs (FXC files).
|
||||
//
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc.
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/shaderprecomp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
if _, err := exec.LookPath("fxc.exe"); err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
fmt.Fprintln(os.Stderr, "fxc.exe not found. Please install Windows SDK.")
|
||||
fmt.Fprintln(os.Stderr, "See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc for more details.")
|
||||
fmt.Fprintln(os.Stderr, "HINT: On PowerShell, you can add a path to the PATH environment variable temporarily like:")
|
||||
fmt.Fprintln(os.Stderr)
|
||||
fmt.Fprintln(os.Stderr, ` & (Get-Process -Id $PID).Path { $env:PATH="C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64;"+$env:PATH; go generate .\examples\shaderprecomp\fxc\ }`)
|
||||
fmt.Fprintln(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
tmpdir, err := os.MkdirTemp("", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
srcs := shaderprecomp.AppendBuildinShaderSources(nil)
|
||||
|
||||
defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcs = append(srcs, shaderprecomp.NewShaderSource(defaultSrcBytes))
|
||||
|
||||
for i, src := range srcs {
|
||||
// Avoid using errgroup.Group.
|
||||
// Compiling sources in parallel causes a mixed error message on the console.
|
||||
if err := compile(src, i, tmpdir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateHSLSFiles(source *shaderprecomp.ShaderSource, index int, tmpdir string) (vs, ps string, err error) {
|
||||
vsHLSLFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d_vs.hlsl", index))
|
||||
vsf, err := os.Create(vsHLSLFilePath)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer vsf.Close()
|
||||
|
||||
psHLSLFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d_ps.hlsl", index))
|
||||
psf, err := os.Create(psHLSLFilePath)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer psf.Close()
|
||||
|
||||
if err := shaderprecomp.CompileToHLSL(vsf, psf, source); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return vsHLSLFilePath, psHLSLFilePath, nil
|
||||
}
|
||||
|
||||
func compile(source *shaderprecomp.ShaderSource, index int, tmpdir string) error {
|
||||
// Generate HLSL files. Make sure this process doesn't have any handlers of the files.
|
||||
// Without closing the files, fxc.exe cannot access the files.
|
||||
vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(source, index, tmpdir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vsFXCFilePath := fmt.Sprintf("%d_vs.fxc", index)
|
||||
cmd := exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLVertexShaderProfile, "/E", shaderprecomp.HLSLVertexShaderEntryPoint, "/Fo", vsFXCFilePath, vsHLSLFilePath)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
psFXCFilePath := fmt.Sprintf("%d_ps.fxc", index)
|
||||
cmd = exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLPixelShaderProfile, "/E", shaderprecomp.HLSLPixelShaderEntryPoint, "/Fo", psFXCFilePath, psHLSLFilePath)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
19
examples/shaderprecomp/fxc/generate.go
Normal file
19
examples/shaderprecomp/fxc/generate.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
package fxc
|
74
examples/shaderprecomp/main.go
Normal file
74
examples/shaderprecomp/main.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
)
|
||||
|
||||
//go:embed defaultshader.go
|
||||
var defaultShaderSourceBytes []byte
|
||||
|
||||
type Game struct {
|
||||
defaultShader *ebiten.Shader
|
||||
counter int
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
g.counter++
|
||||
|
||||
if g.defaultShader == nil {
|
||||
s, err := ebiten.NewShader(defaultShaderSourceBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.defaultShader = s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
cx, cy := ebiten.CursorPosition()
|
||||
w, h := screen.Bounds().Dx(), screen.Bounds().Dy()
|
||||
op := &ebiten.DrawRectShaderOptions{}
|
||||
op.Uniforms = map[string]interface{}{
|
||||
"Time": float32(g.counter) / float32(ebiten.TPS()),
|
||||
"Cursor": []float32{float32(cx), float32(cy)},
|
||||
}
|
||||
screen.DrawRectShader(w, h, g.defaultShader, op)
|
||||
|
||||
msg := `This is a test for shader precompilation.
|
||||
Precompilation works only on macOS so far.
|
||||
Note that this example still works even without shader precompilation.`
|
||||
ebitenutil.DebugPrint(screen, msg)
|
||||
}
|
||||
|
||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
||||
return outsideWidth, outsideHeight
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := registerPrecompiledShaders(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ebiten.SetWindowTitle("Ebitengine Example (Shader Precompilation)")
|
||||
if err := ebiten.RunGame(&Game{}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
1
examples/shaderprecomp/metallib/dummy.metallib
Normal file
1
examples/shaderprecomp/metallib/dummy.metallib
Normal file
@ -0,0 +1 @@
|
||||
This is a dummy .metallib file to trick Go's embed package.
|
93
examples/shaderprecomp/metallib/gen.go
Normal file
93
examples/shaderprecomp/metallib/gen.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
// This is a program to generate precompiled Metal libraries.
|
||||
//
|
||||
// See https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/shaderprecomp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
tmpdir, err := os.MkdirTemp("", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
srcs := shaderprecomp.AppendBuildinShaderSources(nil)
|
||||
|
||||
defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcs = append(srcs, shaderprecomp.NewShaderSource(defaultSrcBytes))
|
||||
|
||||
for i, src := range srcs {
|
||||
// Avoid using errgroup.Group.
|
||||
// Compiling sources in parallel causes a mixed error message on the console.
|
||||
if err := compile(src, i, tmpdir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func compile(source *shaderprecomp.ShaderSource, index int, tmpdir string) error {
|
||||
metalFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d.metal", index))
|
||||
|
||||
f, err := os.Create(metalFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := shaderprecomp.CompileToMSL(f, source); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
irFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d.ir", index))
|
||||
cmd := exec.Command("xcrun", "-sdk", "macosx", "metal", "-o", irFilePath, "-c", metalFilePath)
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metallibFilePath := fmt.Sprintf("%d.metallib", index)
|
||||
cmd = exec.Command("xcrun", "-sdk", "macosx", "metallib", "-o", metallibFilePath, irFilePath)
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
19
examples/shaderprecomp/metallib/generate.go
Normal file
19
examples/shaderprecomp/metallib/generate.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build darwin
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
package metallib
|
48
examples/shaderprecomp/register_darwin.go
Normal file
48
examples/shaderprecomp/register_darwin.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/shaderprecomp"
|
||||
)
|
||||
|
||||
//go:embed metallib/*.metallib
|
||||
var metallibs embed.FS
|
||||
|
||||
func registerPrecompiledShaders() error {
|
||||
srcs := shaderprecomp.AppendBuildinShaderSources(nil)
|
||||
srcs = append(srcs, shaderprecomp.NewShaderSource(defaultShaderSourceBytes))
|
||||
|
||||
for i, src := range srcs {
|
||||
name := fmt.Sprintf("%d.metallib", i)
|
||||
lib, err := metallibs.ReadFile("metallib/" + name)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
fmt.Fprintf(os.Stderr, "precompiled Metal library %s was not found. Run 'go generate' for 'metallib' directory to generate them.\n", name)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
shaderprecomp.RegisterMetalLibrary(src, lib)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
27
examples/shaderprecomp/register_others.go
Normal file
27
examples/shaderprecomp/register_others.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !darwin && !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func registerPrecompiledShaders() error {
|
||||
fmt.Fprintf(os.Stderr, "precompiled shaders are not available in this environment.\n")
|
||||
return nil
|
||||
}
|
61
examples/shaderprecomp/register_windows.go
Normal file
61
examples/shaderprecomp/register_windows.go
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/shaderprecomp"
|
||||
)
|
||||
|
||||
// https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
|
||||
|
||||
//go:embed fxc/*.fxc
|
||||
var fxcs embed.FS
|
||||
|
||||
func registerPrecompiledShaders() error {
|
||||
srcs := shaderprecomp.AppendBuildinShaderSources(nil)
|
||||
srcs = append(srcs, shaderprecomp.NewShaderSource(defaultShaderSourceBytes))
|
||||
|
||||
for i, src := range srcs {
|
||||
vsname := fmt.Sprintf("%d_vs.fxc", i)
|
||||
vs, err := fxcs.ReadFile("fxc/" + vsname)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", vsname)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
psname := fmt.Sprintf("%d_ps.fxc", i)
|
||||
ps, err := fxcs.ReadFile("fxc/" + psname)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", psname)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
shaderprecomp.RegisterFXCs(src, vs, ps)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -141,14 +141,14 @@ func drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) {
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.AntiAlias = aa
|
||||
|
||||
// For strokes (AppendVerticesAndIndicesForStroke), FillAll and NonZero work.
|
||||
// For strokes (AppendVerticesAndIndicesForStroke), FillRuleFillAll and FillRuleNonZero work.
|
||||
//
|
||||
// For filling (AppendVerticesAndIndicesForFilling), NonZero and EvenOdd work.
|
||||
// NonZero and EvenOdd differ when rendering a complex polygons with self-intersections and/or holes.
|
||||
// For filling (AppendVerticesAndIndicesForFilling), FillRuleNonZero and FillRuleEvenOdd work.
|
||||
// FillRuleNonZero and FillRuleEvenOdd differ when rendering a complex polygons with self-intersections and/or holes.
|
||||
// See https://en.wikipedia.org/wiki/Nonzero-rule and https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule .
|
||||
//
|
||||
// For simplicity, this example always uses NonZero, whichever strokes or filling is done.
|
||||
op.FillRule = ebiten.NonZero
|
||||
// For simplicity, this example always uses FillRuleNonZero, whichever strokes or filling is done.
|
||||
op.FillRule = ebiten.FillRuleNonZero
|
||||
|
||||
screen.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
}
|
||||
@ -203,7 +203,7 @@ func drawEbitenLogo(screen *ebiten.Image, x, y int, aa bool, line bool) {
|
||||
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.AntiAlias = aa
|
||||
op.FillRule = ebiten.NonZero
|
||||
op.FillRule = ebiten.FillRuleNonZero
|
||||
screen.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
}
|
||||
|
||||
@ -245,7 +245,7 @@ func drawArc(screen *ebiten.Image, count int, aa bool, line bool) {
|
||||
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.AntiAlias = aa
|
||||
op.FillRule = ebiten.NonZero
|
||||
op.FillRule = ebiten.FillRuleNonZero
|
||||
screen.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
}
|
||||
|
||||
@ -300,7 +300,7 @@ func drawWave(screen *ebiten.Image, counter int, aa bool, line bool) {
|
||||
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.AntiAlias = aa
|
||||
op.FillRule = ebiten.NonZero
|
||||
op.FillRule = ebiten.FillRuleNonZero
|
||||
screen.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
}
|
||||
|
||||
|
16
gameforui.go
16
gameforui.go
@ -25,18 +25,10 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||
)
|
||||
|
||||
var screenFilterEnabled = int32(1)
|
||||
var screenFilterEnabled atomic.Bool
|
||||
|
||||
func isScreenFilterEnabled() bool {
|
||||
return atomic.LoadInt32(&screenFilterEnabled) != 0
|
||||
}
|
||||
|
||||
func setScreenFilterEnabled(enabled bool) {
|
||||
v := int32(0)
|
||||
if enabled {
|
||||
v = 1
|
||||
}
|
||||
atomic.StoreInt32(&screenFilterEnabled, v)
|
||||
func init() {
|
||||
screenFilterEnabled.Store(true)
|
||||
}
|
||||
|
||||
type gameForUI struct {
|
||||
@ -145,7 +137,7 @@ func (g *gameForUI) DrawFinalScreen(scale, offsetX, offsetY float64) {
|
||||
}
|
||||
|
||||
switch {
|
||||
case !isScreenFilterEnabled(), math.Floor(scale) == scale:
|
||||
case !screenFilterEnabled.Load(), math.Floor(scale) == scale:
|
||||
op := &DrawImageOptions{}
|
||||
op.GeoM = geoM
|
||||
g.screen.DrawImage(g.offscreen, op)
|
||||
|
22
go.mod
22
go.mod
@ -1,28 +1,28 @@
|
||||
module github.com/hajimehoshi/ebiten/v2
|
||||
|
||||
go 1.18
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8
|
||||
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895
|
||||
github.com/ebitengine/hideconsole v1.0.0
|
||||
github.com/ebitengine/oto/v3 v3.3.0-alpha.1
|
||||
github.com/ebitengine/purego v0.8.0-alpha.1
|
||||
github.com/ebitengine/purego v0.8.0-alpha.2
|
||||
github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f
|
||||
github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.1.0
|
||||
github.com/go-text/typesetting v0.1.1
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.1
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4
|
||||
github.com/jakecoffman/cp v1.2.1
|
||||
github.com/jezek/xgb v1.1.1
|
||||
github.com/jfreymuth/oggvorbis v1.0.5
|
||||
github.com/kisielk/errcheck v1.7.0
|
||||
golang.org/x/image v0.15.0
|
||||
golang.org/x/sync v0.6.0
|
||||
golang.org/x/sys v0.18.0
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/tools v0.19.0
|
||||
golang.org/x/image v0.16.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/sys v0.20.0
|
||||
golang.org/x/text v0.15.0
|
||||
golang.org/x/tools v0.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/jfreymuth/vorbis v1.0.2 // indirect
|
||||
golang.org/x/mod v0.16.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
)
|
||||
|
40
go.sum
40
go.sum
@ -1,18 +1,18 @@
|
||||
github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8 h1:5e8X7WEdOWrjrKvgaWF6PRnDvJicfrkEnwAkWtMN74g=
|
||||
github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8/go.mod h1:tWboRRNagZwwwis4QIgEFG1ZNFwBJ3LAhSLAXAAxobQ=
|
||||
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU=
|
||||
github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M=
|
||||
github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE=
|
||||
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
|
||||
github.com/ebitengine/oto/v3 v3.3.0-alpha.1 h1:J2nBmQwPLKc4+yLObytq1jKNydI96l6EjZfgefiqGbk=
|
||||
github.com/ebitengine/oto/v3 v3.3.0-alpha.1/go.mod h1:T2/VV0UWG97GEEf4kORMU2nCneYT/YmwSTxPutSVaUg=
|
||||
github.com/ebitengine/purego v0.8.0-alpha.1 h1:52AgJTNaQRi7YtOtdJl4hkxNWhAGMxuDuDjOVIp5Ojk=
|
||||
github.com/ebitengine/purego v0.8.0-alpha.1/go.mod h1:y8L+ZRLphbdPW2xs41fur/KaW57yTzrFsqsclHyHrTM=
|
||||
github.com/ebitengine/purego v0.8.0-alpha.2 h1:+Kyr9n4eXAGMzhtWJxfdQ7AzGn0+6ZWihfCCxul3Dso=
|
||||
github.com/ebitengine/purego v0.8.0-alpha.2/go.mod h1:w5fARo4H5UrAgQTz0yqDfZ6bjstTQwUFmO+TN+nHlWE=
|
||||
github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk=
|
||||
github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRRv/IoHixuZ9bElZnXbmfoUVPGQpdsJ4sVuX38=
|
||||
github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 h1:AqtMl9yw7r319Ah4W2afQm3Ql+PEsQKHds18tGvKhog=
|
||||
github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY=
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.1.0 h1:JLy/na2e83GewqebpFbS2LHpDVnGdzmyJOpqXtBgLm0=
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.1.0/go.mod h1:VVaVK/4HpV1MHWswCl5miFOuLoRVyIplB3qEJxZK2OA=
|
||||
github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo=
|
||||
github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI=
|
||||
github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY=
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.1 h1:GLoMNCbvXOd39tFkqk9w/MI0xSLJaDzEOOl8mT1ILtI=
|
||||
github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.1/go.mod h1:VVaVK/4HpV1MHWswCl5miFOuLoRVyIplB3qEJxZK2OA=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
|
||||
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
|
||||
@ -30,12 +30,13 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjL
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
||||
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
||||
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
|
||||
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
|
||||
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
@ -43,8 +44,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -53,8 +54,8 @@ golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -62,12 +63,13 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
|
||||
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
|
||||
golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
|
||||
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
188
image.go
188
image.go
@ -247,7 +247,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
||||
graphics.QuadVertices(vs, float32(sx0), float32(sy0), float32(sx1), float32(sy1), a, b, c, d, tx, ty, cr, cg, cb, ca)
|
||||
is := graphics.QuadIndices()
|
||||
|
||||
srcs := [graphics.ShaderImageCount]*ui.Image{img.image}
|
||||
srcs := [graphics.ShaderSrcImageCount]*ui.Image{img.image}
|
||||
|
||||
useColorM := !colorm.IsIdentity()
|
||||
shader := builtinShader(filter, builtinshader.AddressUnsafe, useColorM)
|
||||
@ -262,7 +262,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
||||
})
|
||||
}
|
||||
|
||||
i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, canSkipMipmap(geoM, filter), false)
|
||||
i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false)
|
||||
}
|
||||
|
||||
// Vertex represents a vertex passed to DrawTriangles.
|
||||
@ -310,17 +310,36 @@ const (
|
||||
// FillRule is the rule whether an overlapped region is rendered with DrawTriangles(Shader).
|
||||
type FillRule int
|
||||
|
||||
const (
|
||||
// FillRuleFillAll indicates all the triangles are rendered regardless of overlaps.
|
||||
FillRuleFillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll)
|
||||
|
||||
// FillRuleNonZero means that triangles are rendered based on the non-zero rule.
|
||||
// If and only if the number of overlaps is not 0, the region is rendered.
|
||||
FillRuleNonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero)
|
||||
|
||||
// FillRuleEvenOdd means that triangles are rendered based on the even-odd rule.
|
||||
// If and only if the number of overlaps is odd, the region is rendered.
|
||||
FillRuleEvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd)
|
||||
)
|
||||
|
||||
const (
|
||||
// FillAll indicates all the triangles are rendered regardless of overlaps.
|
||||
FillAll FillRule = FillRule(graphicsdriver.FillAll)
|
||||
//
|
||||
// Deprecated: as of v2.8. Use FillRuleFillAll instead.
|
||||
FillAll = FillRuleFillAll
|
||||
|
||||
// NonZero means that triangles are rendered based on the non-zero rule.
|
||||
// If and only if the number of overlaps is not 0, the region is rendered.
|
||||
NonZero FillRule = FillRule(graphicsdriver.NonZero)
|
||||
//
|
||||
// Deprecated: as of v2.8. Use FillRuleNonZero instead.
|
||||
NonZero = FillRuleNonZero
|
||||
|
||||
// EvenOdd means that triangles are rendered based on the even-odd rule.
|
||||
// If and only if the number of overlaps is odd, the region is rendered.
|
||||
EvenOdd FillRule = FillRule(graphicsdriver.EvenOdd)
|
||||
//
|
||||
// Deprecated: as of v2.8. Use FillRuleEvenOdd instead.
|
||||
EvenOdd = FillRuleEvenOdd
|
||||
)
|
||||
|
||||
// ColorScaleMode is the mode of color scales in vertices.
|
||||
@ -371,11 +390,11 @@ type DrawTrianglesOptions struct {
|
||||
|
||||
// FillRule indicates the rule how an overlapped region is rendered.
|
||||
//
|
||||
// The rules NonZero and EvenOdd are useful when you want to render a complex polygon.
|
||||
// The rules FillRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon.
|
||||
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
||||
// See examples/vector for actual usages.
|
||||
//
|
||||
// The default (zero) value is FillAll.
|
||||
// The default (zero) value is FillRuleFillAll.
|
||||
FillRule FillRule
|
||||
|
||||
// AntiAlias indicates whether the rendering uses anti-alias or not.
|
||||
@ -500,7 +519,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
||||
is[i] = uint32(indices[i])
|
||||
}
|
||||
|
||||
srcs := [graphics.ShaderImageCount]*ui.Image{img.image}
|
||||
srcs := [graphics.ShaderSrcImageCount]*ui.Image{img.image}
|
||||
|
||||
useColorM := !colorm.IsIdentity()
|
||||
shader := builtinShader(filter, address, useColorM)
|
||||
@ -515,7 +534,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
||||
})
|
||||
}
|
||||
|
||||
i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), filter != builtinshader.FilterLinear, options.AntiAlias)
|
||||
i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), filter != builtinshader.FilterLinear, options.AntiAlias)
|
||||
}
|
||||
|
||||
// DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
|
||||
@ -547,11 +566,11 @@ type DrawTrianglesShaderOptions struct {
|
||||
|
||||
// FillRule indicates the rule how an overlapped region is rendered.
|
||||
//
|
||||
// The rules NonZero and EvenOdd are useful when you want to render a complex polygon.
|
||||
// The rules FillRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon.
|
||||
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
||||
// See examples/vector for actual usages.
|
||||
//
|
||||
// The default (zero) value is FillAll.
|
||||
// The default (zero) value is FillRuleFillAll.
|
||||
FillRule FillRule
|
||||
|
||||
// AntiAlias indicates whether the rendering uses anti-alias or not.
|
||||
@ -565,7 +584,7 @@ type DrawTrianglesShaderOptions struct {
|
||||
}
|
||||
|
||||
// Check the number of images.
|
||||
var _ [len(DrawTrianglesShaderOptions{}.Images) - graphics.ShaderImageCount]struct{} = [0]struct{}{}
|
||||
var _ [len(DrawTrianglesShaderOptions{}.Images) - graphics.ShaderSrcImageCount]struct{} = [0]struct{}{}
|
||||
|
||||
// DrawTrianglesShader draws triangles with the specified vertices and their indices with the specified shader.
|
||||
//
|
||||
@ -650,7 +669,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
||||
is[i] = uint32(indices[i])
|
||||
}
|
||||
|
||||
var imgs [graphics.ShaderImageCount]*ui.Image
|
||||
var imgs [graphics.ShaderSrcImageCount]*ui.Image
|
||||
var imgSize image.Point
|
||||
for i, img := range options.Images {
|
||||
if img == nil {
|
||||
@ -672,7 +691,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
||||
imgs[i] = img.image
|
||||
}
|
||||
|
||||
var srcRegions [graphics.ShaderImageCount]image.Rectangle
|
||||
var srcRegions [graphics.ShaderSrcImageCount]image.Rectangle
|
||||
for i, img := range options.Images {
|
||||
if img == nil {
|
||||
continue
|
||||
@ -686,6 +705,139 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
||||
i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRule(options.FillRule), true, options.AntiAlias)
|
||||
}
|
||||
|
||||
// DrawTrianglesShader draws triangles with the specified vertices and their indices with the specified shader.
|
||||
//
|
||||
// Vertex contains color values, which can be interpreted for any purpose by the shader.
|
||||
//
|
||||
// For the details about the shader, see https://ebitengine.org/en/documents/shader.html.
|
||||
//
|
||||
// If the shader unit is texels, one of the specified image is non-nil and its size is different from (width, height),
|
||||
// DrawTrianglesShader panics.
|
||||
// If one of the specified image is non-nil and is disposed, DrawTrianglesShader panics.
|
||||
//
|
||||
// If len(vertices) is more than MaxVertexCount, the exceeding part is ignored.
|
||||
//
|
||||
// If len(indices) is not multiple of 3, DrawTrianglesShader panics.
|
||||
//
|
||||
// If a value in indices is out of range of vertices, or not less than MaxVertexCount, DrawTrianglesShader panics.
|
||||
//
|
||||
// When a specified image is non-nil and is disposed, DrawTrianglesShader panics.
|
||||
//
|
||||
// If a specified uniform variable's length or type doesn't match with an expected one, DrawTrianglesShader panics.
|
||||
//
|
||||
// Even if a result is an invalid color as a premultiplied-alpha color, i.e. an alpha value exceeds other color values,
|
||||
// the value is kept and is not clamped.
|
||||
//
|
||||
// When the image i is disposed, DrawTrianglesShader does nothing.
|
||||
func DrawTrianglesShaderMRT(dsts [graphics.ShaderDstImageCount]*Image, vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesShaderOptions) {
|
||||
var dstImgs [graphics.ShaderDstImageCount]*ui.Image
|
||||
var firstDst *Image
|
||||
for i, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
dst.copyCheck()
|
||||
if dst.isDisposed() {
|
||||
panic("ebiten: the destination images given to DrawTrianglesShaderMRT must not be disposed")
|
||||
}
|
||||
if firstDst == nil {
|
||||
firstDst = dst
|
||||
}
|
||||
dstImgs[i] = dst.image
|
||||
}
|
||||
|
||||
if shader.isDisposed() {
|
||||
panic("ebiten: the given shader to DrawTrianglesShaderMRT must not be disposed")
|
||||
}
|
||||
|
||||
if len(vertices) > graphicscommand.MaxVertexCount {
|
||||
// The last part cannot be specified by indices. Just omit them.
|
||||
vertices = vertices[:graphicscommand.MaxVertexCount]
|
||||
}
|
||||
if len(indices)%3 != 0 {
|
||||
panic("ebiten: len(indices) % 3 must be 0")
|
||||
}
|
||||
for i, idx := range indices {
|
||||
if int(idx) >= len(vertices) {
|
||||
panic(fmt.Sprintf("ebiten: indices[%d] must be less than len(vertices) (%d) but was %d", i, len(vertices), idx))
|
||||
}
|
||||
}
|
||||
|
||||
if options == nil {
|
||||
options = &DrawTrianglesShaderOptions{}
|
||||
}
|
||||
|
||||
var blend graphicsdriver.Blend
|
||||
if options.CompositeMode == CompositeModeCustom {
|
||||
blend = options.Blend.internalBlend()
|
||||
} else {
|
||||
blend = options.CompositeMode.blend().internalBlend()
|
||||
}
|
||||
|
||||
dst := firstDst
|
||||
vs := dst.ensureTmpVertices(len(vertices) * graphics.VertexFloatCount)
|
||||
src := options.Images[0]
|
||||
for i, v := range vertices {
|
||||
dx, dy := dst.adjustPositionF32(v.DstX, v.DstY)
|
||||
vs[i*graphics.VertexFloatCount] = dx
|
||||
vs[i*graphics.VertexFloatCount+1] = dy
|
||||
sx, sy := v.SrcX, v.SrcY
|
||||
if src != nil {
|
||||
sx, sy = src.adjustPositionF32(sx, sy)
|
||||
}
|
||||
vs[i*graphics.VertexFloatCount+2] = sx
|
||||
vs[i*graphics.VertexFloatCount+3] = sy
|
||||
vs[i*graphics.VertexFloatCount+4] = v.ColorR
|
||||
vs[i*graphics.VertexFloatCount+5] = v.ColorG
|
||||
vs[i*graphics.VertexFloatCount+6] = v.ColorB
|
||||
vs[i*graphics.VertexFloatCount+7] = v.ColorA
|
||||
}
|
||||
|
||||
is := make([]uint32, len(indices))
|
||||
for i := range is {
|
||||
is[i] = uint32(indices[i])
|
||||
}
|
||||
|
||||
var srcImgs [graphics.ShaderSrcImageCount]*ui.Image
|
||||
var imgSize image.Point
|
||||
for i, img := range options.Images {
|
||||
if img == nil {
|
||||
continue
|
||||
}
|
||||
if img.isDisposed() {
|
||||
panic("ebiten: the given image to DrawTrianglesShader must not be disposed")
|
||||
}
|
||||
if shader.unit == shaderir.Texels {
|
||||
if i == 0 {
|
||||
imgSize = img.Bounds().Size()
|
||||
} else {
|
||||
// TODO: Check imgw > 0 && imgh > 0
|
||||
if img.Bounds().Size() != imgSize {
|
||||
panic("ebiten: all the source images must be the same size with the rectangle")
|
||||
}
|
||||
}
|
||||
}
|
||||
srcImgs[i] = img.image
|
||||
}
|
||||
|
||||
var srcRegions [graphics.ShaderSrcImageCount]image.Rectangle
|
||||
for i, img := range options.Images {
|
||||
if img == nil {
|
||||
continue
|
||||
}
|
||||
srcRegions[i] = img.adjustedBounds()
|
||||
}
|
||||
|
||||
for _, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
dst.tmpUniforms = dst.tmpUniforms[:0]
|
||||
dst.tmpUniforms = shader.appendUniforms(dst.tmpUniforms, options.Uniforms)
|
||||
}
|
||||
ui.DrawTrianglesMRT(dstImgs, srcImgs, vs, is, blend, dst.adjustedBounds(), srcRegions, shader.shader, dst.tmpUniforms, graphicsdriver.FillRule(options.FillRule), true, options.AntiAlias)
|
||||
}
|
||||
|
||||
// DrawRectShaderOptions represents options for DrawRectShader.
|
||||
type DrawRectShaderOptions struct {
|
||||
// GeoM is a geometry matrix to draw.
|
||||
@ -724,7 +876,7 @@ type DrawRectShaderOptions struct {
|
||||
}
|
||||
|
||||
// Check the number of images.
|
||||
var _ [len(DrawRectShaderOptions{}.Images)]struct{} = [graphics.ShaderImageCount]struct{}{}
|
||||
var _ [len(DrawRectShaderOptions{}.Images)]struct{} = [graphics.ShaderSrcImageCount]struct{}{}
|
||||
|
||||
// DrawRectShader draws a rectangle with the specified width and height with the specified shader.
|
||||
//
|
||||
@ -771,7 +923,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
||||
blend = options.CompositeMode.blend().internalBlend()
|
||||
}
|
||||
|
||||
var imgs [graphics.ShaderImageCount]*ui.Image
|
||||
var imgs [graphics.ShaderSrcImageCount]*ui.Image
|
||||
for i, img := range options.Images {
|
||||
if img == nil {
|
||||
continue
|
||||
@ -785,7 +937,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
||||
imgs[i] = img.image
|
||||
}
|
||||
|
||||
var srcRegions [graphics.ShaderImageCount]image.Rectangle
|
||||
var srcRegions [graphics.ShaderSrcImageCount]image.Rectangle
|
||||
for i, img := range options.Images {
|
||||
if img == nil {
|
||||
if shader.unit == shaderir.Pixels && i == 0 {
|
||||
@ -816,7 +968,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
||||
i.tmpUniforms = i.tmpUniforms[:0]
|
||||
i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms)
|
||||
|
||||
i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, true, false)
|
||||
i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false)
|
||||
}
|
||||
|
||||
// SubImage returns an image representing the portion of the image p visible through r.
|
||||
|
@ -2696,7 +2696,7 @@ func TestImageEvenOdd(t *testing.T) {
|
||||
// Draw all the vertices once. The even-odd rule is applied for all the vertices once.
|
||||
dst := ebiten.NewImage(16, 16)
|
||||
op := &ebiten.DrawTrianglesOptions{
|
||||
FillRule: ebiten.EvenOdd,
|
||||
FillRule: ebiten.FillRuleEvenOdd,
|
||||
}
|
||||
dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op)
|
||||
for j := 0; j < 16; j++ {
|
||||
@ -2794,15 +2794,15 @@ func TestImageEvenOdd(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestImageFillRule(t *testing.T) {
|
||||
for _, fillRule := range []ebiten.FillRule{ebiten.FillAll, ebiten.NonZero, ebiten.EvenOdd} {
|
||||
for _, fillRule := range []ebiten.FillRule{ebiten.FillRuleFillAll, ebiten.FillRuleNonZero, ebiten.FillRuleEvenOdd} {
|
||||
fillRule := fillRule
|
||||
var name string
|
||||
switch fillRule {
|
||||
case ebiten.FillAll:
|
||||
case ebiten.FillRuleFillAll:
|
||||
name = "FillAll"
|
||||
case ebiten.NonZero:
|
||||
case ebiten.FillRuleNonZero:
|
||||
name = "NonZero"
|
||||
case ebiten.EvenOdd:
|
||||
case ebiten.FillRuleEvenOdd:
|
||||
name = "EvenOdd"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
@ -2885,11 +2885,11 @@ func TestImageFillRule(t *testing.T) {
|
||||
var want color.RGBA
|
||||
switch {
|
||||
case 2 <= i && i < 7 && 2 <= j && j < 7:
|
||||
if fillRule != ebiten.EvenOdd {
|
||||
if fillRule != ebiten.FillRuleEvenOdd {
|
||||
want = color.RGBA{G: 0xff, A: 0xff}
|
||||
}
|
||||
case 9 <= i && i < 14 && 9 <= j && j < 14:
|
||||
if fillRule == ebiten.FillAll {
|
||||
if fillRule == ebiten.FillRuleFillAll {
|
||||
want = color.RGBA{B: 0xff, A: 0xff}
|
||||
}
|
||||
case 1 <= i && i < 15 && 1 <= j && j < 15:
|
||||
@ -2922,11 +2922,11 @@ func TestImageFillRule(t *testing.T) {
|
||||
var want color.RGBA
|
||||
switch {
|
||||
case 3 <= i && i < 8 && 3 <= j && j < 8:
|
||||
if fillRule != ebiten.EvenOdd {
|
||||
if fillRule != ebiten.FillRuleEvenOdd {
|
||||
want = color.RGBA{G: 0xff, A: 0xff}
|
||||
}
|
||||
case 10 <= i && i < 15 && 10 <= j && j < 15:
|
||||
if fillRule == ebiten.FillAll {
|
||||
if fillRule == ebiten.FillRuleFillAll {
|
||||
want = color.RGBA{B: 0xff, A: 0xff}
|
||||
}
|
||||
case 2 <= i && i < 16 && 2 <= j && j < 16:
|
||||
@ -3726,7 +3726,7 @@ func TestImageTooManyConstantBuffersInDirectX(t *testing.T) {
|
||||
dst0 := ebiten.NewImage(16, 16)
|
||||
dst1 := ebiten.NewImage(16, 16)
|
||||
op := &ebiten.DrawTrianglesOptions{
|
||||
FillRule: ebiten.EvenOdd,
|
||||
FillRule: ebiten.FillRuleEvenOdd,
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
dst0.DrawTriangles(vs, is, src, op)
|
||||
|
@ -14,16 +14,12 @@
|
||||
|
||||
package atlas
|
||||
|
||||
import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||
)
|
||||
|
||||
const (
|
||||
BaseCountToPutOnSourceBackend = baseCountToPutOnSourceBackend
|
||||
)
|
||||
|
||||
func PutImagesOnSourceBackendForTesting(graphicsDriver graphicsdriver.Graphics) {
|
||||
putImagesOnSourceBackend(graphicsDriver)
|
||||
func PutImagesOnSourceBackendForTesting() {
|
||||
putImagesOnSourceBackend()
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -36,13 +36,6 @@ var (
|
||||
maxSize = 0
|
||||
)
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
@ -81,7 +74,7 @@ func flushDeferred() {
|
||||
// Actual time duration is increased in an exponential way for each usage as a rendering target.
|
||||
const baseCountToPutOnSourceBackend = 10
|
||||
|
||||
func putImagesOnSourceBackend(graphicsDriver graphicsdriver.Graphics) {
|
||||
func putImagesOnSourceBackend() {
|
||||
// The counter usedAsDestinationCount is updated at most once per frame (#2676).
|
||||
imagesUsedAsDestination.forEach(func(i *Image) {
|
||||
// This counter is not updated when the backend is created in this frame.
|
||||
@ -97,7 +90,7 @@ func putImagesOnSourceBackend(graphicsDriver graphicsdriver.Graphics) {
|
||||
i.usedAsSourceCount++
|
||||
}
|
||||
if int64(i.usedAsSourceCount) >= int64(baseCountToPutOnSourceBackend*(1<<uint(min(i.usedAsDestinationCount, 31)))) {
|
||||
i.putOnSourceBackend(graphicsDriver)
|
||||
i.putOnSourceBackend()
|
||||
i.usedAsSourceCount = 0
|
||||
}
|
||||
})
|
||||
@ -153,12 +146,12 @@ func (b *backend) extendIfNeeded(width, height int) {
|
||||
|
||||
// Use DrawTriangles instead of WritePixels because the image i might be stale and not have its pixels
|
||||
// information.
|
||||
srcs := [graphics.ShaderImageCount]*graphicscommand.Image{b.image}
|
||||
srcs := [graphics.ShaderSrcImageCount]*graphicscommand.Image{b.image}
|
||||
sw, sh := b.image.InternalSize()
|
||||
vs := quadVertices(0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, sw, sh)
|
||||
newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillAll)
|
||||
newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll)
|
||||
b.image.Dispose()
|
||||
|
||||
b.image = newImg
|
||||
@ -182,7 +175,7 @@ func newClearedImage(width, height int, screen bool) *graphicscommand.Image {
|
||||
func clearImage(i *graphicscommand.Image, region image.Rectangle) {
|
||||
vs := quadVertices(float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillAll)
|
||||
i.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll)
|
||||
}
|
||||
|
||||
func (b *backend) clearPixels(region image.Rectangle) {
|
||||
@ -355,11 +348,11 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) {
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, i.width, i.height)
|
||||
|
||||
newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
newI.moveTo(i)
|
||||
}
|
||||
|
||||
func (i *Image) putOnSourceBackend(graphicsDriver graphicsdriver.Graphics) {
|
||||
func (i *Image) putOnSourceBackend() {
|
||||
if i.backend == nil {
|
||||
i.allocate(nil, true)
|
||||
return
|
||||
@ -385,7 +378,7 @@ func (i *Image) putOnSourceBackend(graphicsDriver graphicsdriver.Graphics) {
|
||||
graphics.QuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, i.width, i.height)
|
||||
newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
newI.moveTo(i)
|
||||
i.usedAsSourceCount = 0
|
||||
@ -417,7 +410,7 @@ func (i *Image) regionWithPadding() image.Rectangle {
|
||||
// 5: Color G
|
||||
// 6: Color B
|
||||
// 7: Color Y
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
|
||||
@ -438,11 +431,33 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
i.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
}
|
||||
|
||||
func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
func DrawTrianglesMRT(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
|
||||
if !inFrame {
|
||||
vs := make([]float32, len(vertices))
|
||||
copy(vs, vertices)
|
||||
is := make([]uint32, len(indices))
|
||||
copy(is, indices)
|
||||
us := make([]uint32, len(uniforms))
|
||||
copy(us, uniforms)
|
||||
|
||||
appendDeferred(func() {
|
||||
drawTrianglesMRT(dsts, srcs, vs, is, blend, dstRegion, srcRegions, shader, us, fillRule)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
drawTrianglesMRT(dsts, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
}
|
||||
|
||||
func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
if len(vertices) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// This slice is not escaped to the heap. This can be checked by `go build -gcflags=-m`.
|
||||
backends := make([]*backend, 0, len(srcs))
|
||||
for _, src := range srcs {
|
||||
if src == nil {
|
||||
@ -515,7 +530,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
srcRegions[i] = srcRegions[i].Add(r.Min)
|
||||
}
|
||||
|
||||
var imgs [graphics.ShaderImageCount]*graphicscommand.Image
|
||||
var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image
|
||||
for i, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
@ -536,6 +551,115 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
}
|
||||
}
|
||||
|
||||
func drawTrianglesMRT(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
if len(vertices) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
backends := make([]*backend, 0, len(srcs))
|
||||
for _, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
if src.backend == nil {
|
||||
// It is possible to spcify i.backend as a forbidden backend, but this might prevent a good allocation for a source image.
|
||||
// If the backend becomes the same as i's, i's backend will be changed at ensureIsolatedFromSource.
|
||||
src.allocate(nil, true)
|
||||
}
|
||||
backends = append(backends, src.backend)
|
||||
src.backend.sourceInThisFrame = true
|
||||
}
|
||||
|
||||
var firstDst *Image
|
||||
var dstImgs [graphics.ShaderDstImageCount]*graphicscommand.Image
|
||||
for i, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
dst.ensureIsolatedFromSource(backends)
|
||||
firstDst = dst
|
||||
dstImgs[i] = dst.backend.image
|
||||
}
|
||||
|
||||
for _, src := range srcs {
|
||||
// Compare i and source images after ensuring i is not on an atlas, or
|
||||
// i and a source image might share the same atlas even though i != src.
|
||||
for _, dst := range dsts {
|
||||
if src != nil && dst != nil && dst.backend.image == src.backend.image {
|
||||
panic("atlas: DrawTrianglesMRT: source must be different from the destination images")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r := firstDst.regionWithPadding()
|
||||
// TODO: Check if dstRegion does not to violate the region.
|
||||
dstRegion = dstRegion.Add(r.Min)
|
||||
|
||||
dx, dy := float32(r.Min.X), float32(r.Min.Y)
|
||||
|
||||
var oxf, oyf float32
|
||||
if srcs[0] != nil {
|
||||
r := srcs[0].regionWithPadding()
|
||||
oxf, oyf = float32(r.Min.X), float32(r.Min.Y)
|
||||
n := len(vertices)
|
||||
for i := 0; i < n; i += graphics.VertexFloatCount {
|
||||
vertices[i] += dx
|
||||
vertices[i+1] += dy
|
||||
vertices[i+2] += oxf
|
||||
vertices[i+3] += oyf
|
||||
}
|
||||
if shader.ir.Unit == shaderir.Texels {
|
||||
sw, sh := srcs[0].backend.image.InternalSize()
|
||||
swf, shf := float32(sw), float32(sh)
|
||||
for i := 0; i < n; i += graphics.VertexFloatCount {
|
||||
vertices[i+2] /= swf
|
||||
vertices[i+3] /= shf
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n := len(vertices)
|
||||
for i := 0; i < n; i += graphics.VertexFloatCount {
|
||||
vertices[i] += dx
|
||||
vertices[i+1] += dy
|
||||
}
|
||||
}
|
||||
|
||||
for i, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// A source region can be deliberately empty when this is not needed in order to avoid unexpected
|
||||
// performance issue (#1293).
|
||||
if srcRegions[i].Empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
r := src.regionWithPadding()
|
||||
srcRegions[i] = srcRegions[i].Add(r.Min)
|
||||
}
|
||||
|
||||
var srcImgs [graphics.ShaderSrcImageCount]*graphicscommand.Image
|
||||
for i, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
srcImgs[i] = src.backend.image
|
||||
}
|
||||
|
||||
graphicscommand.DrawTrianglesMRT(dstImgs, srcImgs, vertices, indices, blend, dstRegion, srcRegions, shader.ensureShader(), uniforms, fillRule)
|
||||
|
||||
for _, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
if !src.isOnSourceBackend() && src.canBePutOnAtlas() {
|
||||
// src might already registered, but assigning it again is not harmful.
|
||||
imagesToPutOnSourceBackend.add(src)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WritePixels replaces the pixels on the image.
|
||||
func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
|
||||
backendsM.Lock()
|
||||
@ -942,7 +1066,7 @@ func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
|
||||
}
|
||||
|
||||
flushDeferred()
|
||||
putImagesOnSourceBackend(graphicsDriver)
|
||||
putImagesOnSourceBackend()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
|
||||
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, size, size)
|
||||
img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img4.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -113,7 +113,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
|
||||
// img5 is not allocated now, but is allocated at DrawTriangles.
|
||||
vs = quadVertices(0, 0, size/2, size/2, 1)
|
||||
dr = image.Rect(0, 0, size/2, size/2)
|
||||
img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img3.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img3.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -147,7 +147,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
|
||||
// Check further drawing doesn't cause panic.
|
||||
// This bug was fixed by 03dcd948.
|
||||
vs = quadVertices(0, 0, size/2, size/2, 1)
|
||||
img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
}
|
||||
|
||||
func TestReputOnSourceBackend(t *testing.T) {
|
||||
@ -191,7 +191,7 @@ func TestReputOnSourceBackend(t *testing.T) {
|
||||
// Render onto img1. The count should not matter.
|
||||
for i := 0; i < 5; i++ {
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -201,21 +201,21 @@ func TestReputOnSourceBackend(t *testing.T) {
|
||||
// Use the doubled count since img1 was on a texture atlas and became an isolated image once.
|
||||
// Then, img1 requires longer time to recover to be on a texture atlas again.
|
||||
for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ {
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
}
|
||||
// Finally, img1 is on a source backend.
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img1.IsOnSourceBackendForTesting(), true; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
|
||||
pix = make([]byte, 4*size*size)
|
||||
ok, err := img1.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, size, size))
|
||||
@ -240,7 +240,7 @@ func TestReputOnSourceBackend(t *testing.T) {
|
||||
}
|
||||
|
||||
vs = quadVertices(size, size, 0, 0, 1)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img1.IsOnSourceBackendForTesting(), true; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -270,7 +270,7 @@ func TestReputOnSourceBackend(t *testing.T) {
|
||||
// Use img1 as a render target again. The count should not matter.
|
||||
for i := 0; i < 5; i++ {
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -279,28 +279,28 @@ func TestReputOnSourceBackend(t *testing.T) {
|
||||
// Use img1 as a render source, but call WritePixels.
|
||||
// Now use 4x count as img1 became an isolated image again.
|
||||
for i := 0; i < atlas.BaseCountToPutOnSourceBackend*4; i++ {
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size))
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
}
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
|
||||
// img1 is not on an atlas due to WritePixels.
|
||||
vs = quadVertices(size, size, 0, 0, 1)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
|
||||
// Use img3 as a render source. As img3 is volatile, img3 is never on an atlas.
|
||||
for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ {
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := img3.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -403,7 +403,7 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) {
|
||||
vs := quadVertices(w, h, 0, 0, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
dst.WritePixels(pix, image.Rect(0, 0, w, h))
|
||||
|
||||
pix = make([]byte, 4*w*h)
|
||||
@ -450,7 +450,7 @@ func TestSmallImages(t *testing.T) {
|
||||
vs := quadVertices(w, h, 0, 0, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
pix = make([]byte, 4*w*h)
|
||||
ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h))
|
||||
@ -497,7 +497,7 @@ func TestLongImages(t *testing.T) {
|
||||
vs := quadVertices(w, h, 0, 0, scale)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, dstW, dstH)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
pix = make([]byte, 4*dstW*dstH)
|
||||
ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, dstW, dstH))
|
||||
@ -613,16 +613,16 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) {
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, size, size)
|
||||
src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
src.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := src.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
|
||||
// Use src as a render source.
|
||||
for i := 0; i < atlas.BaseCountToPutOnSourceBackend/2; i++ {
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := src.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -632,7 +632,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) {
|
||||
src.Deallocate()
|
||||
|
||||
// Confirm that PutImagesOnSourceBackendForTesting doesn't panic.
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
}
|
||||
|
||||
// Issue #1456
|
||||
@ -656,7 +656,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) {
|
||||
// Call DrawTriangles multiple times.
|
||||
// The number of DrawTriangles doesn't matter as long as these are called in one frame.
|
||||
for i := 0; i < 2; i++ {
|
||||
src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
src2.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
}
|
||||
if got, want := src2.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
@ -665,7 +665,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) {
|
||||
// Update the count without using src2 as a rendering source.
|
||||
// This should not affect whether src2 is on an atlas or not.
|
||||
for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ {
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
if got, want := src2.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -673,15 +673,15 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) {
|
||||
|
||||
// Update the count with using src2 as a rendering source.
|
||||
for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ {
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
vs := quadVertices(size, size, 0, 0, 1)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
if got, want := src2.IsOnSourceBackendForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
if got, want := src2.IsOnSourceBackendForTesting(), true; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -801,15 +801,15 @@ func TestDestinationCountOverflow(t *testing.T) {
|
||||
|
||||
// Use dst0 as a destination for a while.
|
||||
for i := 0; i < 31; i++ {
|
||||
dst0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
dst0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
}
|
||||
|
||||
// Use dst0 as a source for a while.
|
||||
// As dst0 is used as a destination too many times (31 is a maximum), dst0's backend should never be a source backend.
|
||||
for i := 0; i < 100; i++ {
|
||||
dst1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
dst1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
if dst0.IsOnSourceBackendForTesting() {
|
||||
t.Errorf("dst0 cannot be on a source backend: %d", i)
|
||||
}
|
||||
@ -834,17 +834,17 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) {
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
for _, img := range srcs {
|
||||
img.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
img.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
}
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
|
||||
// Use srcs as sources. This will register an image to imagesToPutOnSourceBackend.
|
||||
// Check iterating the registered image works correctly.
|
||||
for i := 0; i < 100; i++ {
|
||||
for _, src := range srcs {
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
}
|
||||
atlas.PutImagesOnSourceBackendForTesting(ui.Get().GraphicsDriverForTesting())
|
||||
atlas.PutImagesOnSourceBackendForTesting()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,13 +104,7 @@ func init() {
|
||||
return nil
|
||||
})
|
||||
wg.Go(func() error {
|
||||
ir, err := graphics.CompileShader([]byte(`//kage:unit pixels
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
return vec4(0)
|
||||
}`))
|
||||
ir, err := graphics.CompileShader([]byte(builtinshader.ClearShaderSource))
|
||||
if err != nil {
|
||||
return fmt.Errorf("atlas: compiling the clear shader failed: %w", err)
|
||||
}
|
||||
|
@ -37,12 +37,12 @@ func TestShaderFillTwice(t *testing.T) {
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
g := ui.Get().GraphicsDriverForTesting()
|
||||
s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff))
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
// Vertices must be recreated (#1755)
|
||||
vs = quadVertices(w, h, 0, 0, 1)
|
||||
s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff))
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
pix := make([]byte, 4*w*h)
|
||||
ok, err := dst.ReadPixels(g, pix, image.Rect(0, 0, w, h))
|
||||
@ -69,11 +69,11 @@ func TestImageDrawTwice(t *testing.T) {
|
||||
vs := quadVertices(w, h, 0, 0, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
// Vertices must be recreated (#1755)
|
||||
vs = quadVertices(w, h, 0, 0, 1)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
pix := make([]byte, 4*w*h)
|
||||
ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h))
|
||||
@ -97,7 +97,7 @@ func TestGCShader(t *testing.T) {
|
||||
vs := quadVertices(w, h, 0, 0, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
// Ensure other objects are GCed, as GC appends deferred functions for collected objects.
|
||||
ensureGC()
|
||||
|
@ -183,7 +183,7 @@ func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
|
||||
// DrawTriangles draws the src image with the given vertices.
|
||||
//
|
||||
// Copying vertices and indices is the caller's responsibility.
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
for _, src := range srcs {
|
||||
if i == src {
|
||||
panic("buffered: Image.DrawTriangles: source images must be different from the receiver")
|
||||
@ -197,7 +197,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
|
||||
i.syncPixelsIfNeeded()
|
||||
|
||||
var imgs [graphics.ShaderImageCount]*atlas.Image
|
||||
var imgs [graphics.ShaderSrcImageCount]*atlas.Image
|
||||
for i, img := range srcs {
|
||||
if img == nil {
|
||||
continue
|
||||
@ -211,6 +211,51 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
i.pixels = nil
|
||||
}
|
||||
|
||||
func DrawTrianglesMRT(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
for _, src := range srcs {
|
||||
for _, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
if dst == src {
|
||||
panic("buffered: DrawTrianglesMRT: source images must be different from the destination images")
|
||||
}
|
||||
}
|
||||
if src != nil {
|
||||
// src's pixels have to be synced between CPU and GPU,
|
||||
// but doesn't have to be cleared since src is not modified in this function.
|
||||
src.syncPixelsIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
var dstImgs [graphics.ShaderDstImageCount]*atlas.Image
|
||||
for i, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
dst.syncPixelsIfNeeded()
|
||||
dstImgs[i] = dst.img
|
||||
}
|
||||
|
||||
var srcImgs [graphics.ShaderSrcImageCount]*atlas.Image
|
||||
for i, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
srcImgs[i] = src.img
|
||||
}
|
||||
|
||||
atlas.DrawTrianglesMRT(dstImgs, srcImgs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
|
||||
// After rendering, the pixel cache is no longer valid.
|
||||
for _, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
dst.pixels = nil
|
||||
}
|
||||
}
|
||||
|
||||
// syncPixelsIfNeeded syncs the pixels between CPU and GPU.
|
||||
// After syncPixelsIfNeeded, dotsBuffer is cleared, but pixels might remain.
|
||||
func (i *Image) syncPixelsIfNeeded() {
|
||||
@ -289,10 +334,10 @@ func (i *Image) syncPixelsIfNeeded() {
|
||||
idx++
|
||||
}
|
||||
|
||||
srcs := [graphics.ShaderImageCount]*atlas.Image{whiteImage.img}
|
||||
srcs := [graphics.ShaderSrcImageCount]*atlas.Image{whiteImage.img}
|
||||
dr := image.Rect(0, 0, i.width, i.height)
|
||||
blend := graphicsdriver.BlendCopy
|
||||
i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
// TODO: Use clear if Go 1.21 is available.
|
||||
for pos := range i.dotsBuffer {
|
||||
|
@ -55,8 +55,8 @@ func TestUnsyncedPixels(t *testing.T) {
|
||||
graphics.QuadVertices(vs, 0, 0, 16, 16, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, 16, 16)
|
||||
sr := [graphics.ShaderImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)}
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
sr := [graphics.ShaderSrcImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)}
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
// Check the result is correct.
|
||||
var got [4]byte
|
||||
|
@ -188,3 +188,22 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
return mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y)
|
||||
}
|
||||
`)
|
||||
|
||||
var ClearShaderSource = []byte(`//kage:unit pixels
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
return vec4(0)
|
||||
}
|
||||
`)
|
||||
|
||||
func AppendShaderSources(sources [][]byte) [][]byte {
|
||||
for filter := Filter(0); filter < FilterCount; filter++ {
|
||||
for address := Address(0); address < AddressCount; address++ {
|
||||
sources = append(sources, ShaderSource(filter, address, false), ShaderSource(filter, address, true))
|
||||
}
|
||||
}
|
||||
sources = append(sources, ScreenShaderSource, ClearShaderSource)
|
||||
return sources
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ type nativeGamepadsDesktop struct {
|
||||
enumObjectsCallback uintptr
|
||||
|
||||
nativeWindow windows.HWND
|
||||
deviceChanged int32
|
||||
deviceChanged atomic.Bool
|
||||
err error
|
||||
}
|
||||
|
||||
@ -537,11 +537,11 @@ func (g *nativeGamepadsDesktop) update(gamepads *gamepads) error {
|
||||
g.origWndProc = h
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&g.deviceChanged) != 0 {
|
||||
if g.deviceChanged.Load() {
|
||||
if err := g.detectConnection(gamepads); err != nil {
|
||||
g.err = err
|
||||
}
|
||||
atomic.StoreInt32(&g.deviceChanged, 0)
|
||||
g.deviceChanged.Store(false)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -550,7 +550,7 @@ func (g *nativeGamepadsDesktop) update(gamepads *gamepads) error {
|
||||
func (g *nativeGamepadsDesktop) wndProc(hWnd uintptr, uMsg uint32, wParam, lParam uintptr) uintptr {
|
||||
switch uMsg {
|
||||
case _WM_DEVICECHANGE:
|
||||
atomic.StoreInt32(&g.deviceChanged, 1)
|
||||
g.deviceChanged.Store(true)
|
||||
}
|
||||
return _CallWindowProcW(g.origWndProc, hWnd, uMsg, wParam, lParam)
|
||||
}
|
||||
|
@ -19,8 +19,9 @@ func _glfwPlatformCreateTls(tls *C._GLFWtls) C.GLFWbool {
|
||||
panic("glfw: TLS must not be allocated")
|
||||
}
|
||||
if pthread_key_create(&tls.posix.key, 0) != 0 {
|
||||
_glfwInputError(int32(PlatformError),
|
||||
C.CString("POSIX: Failed to create context TLS"))
|
||||
errstr := C.CString("POSIX: Failed to create context TLS")
|
||||
defer C.free(unsafe.Pointer(errstr))
|
||||
_glfwInputError(int32(PlatformError), errstr)
|
||||
return False
|
||||
}
|
||||
tls.posix.allocated = True
|
||||
@ -58,7 +59,9 @@ func _glfwPlatformCreateMutex(mutex *C._GLFWmutex) C.GLFWbool {
|
||||
panic("glfw: mutex must not be allocated")
|
||||
}
|
||||
if pthread_mutex_init(&mutex.posix.handle, nil) != 0 {
|
||||
_glfwInputError(int32(PlatformError), C.CString("POSIX: Failed to create mutex"))
|
||||
errstr := C.CString("POSIX: Failed to create mutex")
|
||||
defer C.free(unsafe.Pointer(errstr))
|
||||
_glfwInputError(int32(PlatformError), errstr)
|
||||
return False
|
||||
}
|
||||
mutex.posix.allocated = True
|
||||
|
@ -261,18 +261,23 @@ func makeContextCurrentWGL(window *Window) error {
|
||||
}
|
||||
|
||||
func swapBuffersWGL(window *Window) error {
|
||||
if window.monitor == nil {
|
||||
// HACK: Use DwmFlush when desktop composition is enabled on Windows Vista and 7
|
||||
if !winver.IsWindows8OrGreater() && winver.IsWindowsVistaOrGreater() {
|
||||
enabled, err := _DwmIsCompositionEnabled()
|
||||
if window.monitor == nil && winver.IsWindowsVistaOrGreater() {
|
||||
// DWM Composition is always enabled on Win8+
|
||||
enabled := winver.IsWindows8OrGreater()
|
||||
|
||||
if !enabled {
|
||||
var err error
|
||||
enabled, err = _DwmIsCompositionEnabled()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if enabled {
|
||||
for i := 0; i < window.context.platform.interval; i++ {
|
||||
// Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113).
|
||||
_ = _DwmFlush()
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Use DwmFlush when desktop composition is enabled
|
||||
if enabled {
|
||||
for i := 0; i < window.context.platform.interval; i++ {
|
||||
// Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113).
|
||||
_ = _DwmFlush()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -292,19 +297,23 @@ func swapIntervalWGL(interval int) error {
|
||||
|
||||
window.context.platform.interval = interval
|
||||
|
||||
if window.monitor == nil {
|
||||
// HACK: Disable WGL swap interval when desktop composition is enabled on Windows
|
||||
// Vista and 7 to avoid interfering with DWM vsync
|
||||
if !winver.IsWindows8OrGreater() && winver.IsWindowsVistaOrGreater() {
|
||||
enabled, err := _DwmIsCompositionEnabled()
|
||||
if window.monitor == nil && winver.IsWindowsVistaOrGreater() {
|
||||
// DWM Composition is always enabled on Win8+
|
||||
enabled := winver.IsWindows8OrGreater()
|
||||
|
||||
if !enabled {
|
||||
e, err := _DwmIsCompositionEnabled()
|
||||
// Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113).
|
||||
if err == nil {
|
||||
enabled = false
|
||||
}
|
||||
if enabled {
|
||||
interval = 0
|
||||
enabled = e
|
||||
}
|
||||
}
|
||||
|
||||
// HACK: Disable WGL swap interval when desktop composition is enabled to
|
||||
// avoid interfering with DWM vsync
|
||||
if enabled {
|
||||
interval = 0
|
||||
}
|
||||
}
|
||||
|
||||
if _glfw.platformContext.EXT_swap_control {
|
||||
|
@ -2309,7 +2309,7 @@ func (w *Window) platformSetCursorMode(mode int) error {
|
||||
|
||||
if mode == CursorDisabled {
|
||||
_glfw.platformWindow.disabledCursorWindow = w
|
||||
} else {
|
||||
} else if _glfw.platformWindow.disabledCursorWindow == w {
|
||||
_glfw.platformWindow.disabledCursorWindow = nil
|
||||
if err := w.platformSetCursorPos(_glfw.platformWindow.restoreCursorPosX, _glfw.platformWindow.restoreCursorPosY); err != nil {
|
||||
return err
|
||||
|
@ -89,9 +89,9 @@ var __imageSrcRegionSizes [%[1]d]vec2
|
||||
func imageSrcRegionOnTexture() (vec2, vec2) {
|
||||
return __imageSrcRegionOrigins[0], __imageSrcRegionSizes[0]
|
||||
}
|
||||
`, ShaderImageCount)
|
||||
`, ShaderSrcImageCount)
|
||||
|
||||
for i := 0; i < ShaderImageCount; i++ {
|
||||
for i := 0; i < ShaderSrcImageCount; i++ {
|
||||
shaderSuffix += fmt.Sprintf(`
|
||||
// imageSrc%[1]dOrigin returns the source image's region origin on its texture.
|
||||
// The unit is the source texture's pixel or texel.
|
||||
@ -161,8 +161,8 @@ func __vertex(dstPos vec2, srcPos vec2, color vec4) (vec4, vec2, vec4) {
|
||||
return shaderSuffix, nil
|
||||
}
|
||||
|
||||
func CompileShader(src []byte) (*shaderir.Program, error) {
|
||||
unit, err := shader.ParseCompilerDirectives(src)
|
||||
func completeShaderSource(fragmentSrc []byte) ([]byte, error) {
|
||||
unit, err := shader.ParseCompilerDirectives(fragmentSrc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -172,14 +172,23 @@ func CompileShader(src []byte) (*shaderir.Program, error) {
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write(src)
|
||||
buf.Write(fragmentSrc)
|
||||
buf.WriteString(suffix)
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func CompileShader(fragmentSrc []byte) (*shaderir.Program, error) {
|
||||
src, err := completeShaderSource(fragmentSrc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
const (
|
||||
vert = "__vertex"
|
||||
frag = "Fragment"
|
||||
)
|
||||
ir, err := shader.Compile(buf.Bytes(), vert, frag, ShaderImageCount)
|
||||
ir, err := shader.Compile(src, vert, frag, ShaderSrcImageCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -193,3 +202,11 @@ func CompileShader(src []byte) (*shaderir.Program, error) {
|
||||
|
||||
return ir, nil
|
||||
}
|
||||
|
||||
func CalcSourceHash(fragmentSrc []byte) (shaderir.SourceHash, error) {
|
||||
src, err := completeShaderSource(fragmentSrc)
|
||||
if err != nil {
|
||||
return shaderir.SourceHash{}, err
|
||||
}
|
||||
return shaderir.CalcSourceHash(src), nil
|
||||
}
|
||||
|
@ -15,7 +15,12 @@
|
||||
package graphics
|
||||
|
||||
const (
|
||||
ShaderImageCount = 4
|
||||
ShaderSrcImageCount = 4
|
||||
// The minimum guaranteed value for the number of target seems to be 8
|
||||
// OpenGL(8): https://www.khronos.org/opengl/wiki/Framebuffer_Object#Framebuffer_Object_Structure
|
||||
// DirectX11(8): https://learn.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage#multiple-rendertargets-overview
|
||||
// Metal(8): Page 7 of 15: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||
ShaderDstImageCount = 8
|
||||
|
||||
// PreservedUniformVariablesCount represents the number of preserved uniform variables.
|
||||
// Any shaders in Ebitengine must have these uniform variables.
|
||||
@ -30,11 +35,11 @@ const (
|
||||
ProjectionMatrixUniformVariableIndex = 6
|
||||
|
||||
PreservedUniformUint32Count = 2 + // the destination texture size
|
||||
2*ShaderImageCount + // the source texture sizes array
|
||||
2*ShaderSrcImageCount + // the source texture sizes array
|
||||
2 + // the destination image region origin
|
||||
2 + // the destination image region size
|
||||
2*ShaderImageCount + // the source image region origins array
|
||||
2*ShaderImageCount + // the source image region sizes array
|
||||
2*ShaderSrcImageCount + // the source image region origins array
|
||||
2*ShaderSrcImageCount + // the source image region sizes array
|
||||
16 // the projection matrix
|
||||
)
|
||||
|
||||
|
@ -61,8 +61,8 @@ func (p *drawTrianglesCommandPool) put(v *drawTrianglesCommand) {
|
||||
|
||||
// drawTrianglesCommand represents a drawing command to draw an image on another image.
|
||||
type drawTrianglesCommand struct {
|
||||
dst *Image
|
||||
srcs [graphics.ShaderImageCount]*Image
|
||||
dsts [graphics.ShaderDstImageCount]*Image
|
||||
srcs [graphics.ShaderSrcImageCount]*Image
|
||||
vertices []float32
|
||||
blend graphicsdriver.Blend
|
||||
dstRegions []graphicsdriver.DstRegion
|
||||
@ -81,12 +81,19 @@ func (c *drawTrianglesCommand) String() string {
|
||||
c.blend.BlendOperationRGB,
|
||||
c.blend.BlendOperationAlpha)
|
||||
|
||||
dst := fmt.Sprintf("%d", c.dst.id)
|
||||
if c.dst.screen {
|
||||
dst += " (screen)"
|
||||
var dststrs [graphics.ShaderDstImageCount]string
|
||||
for i, dst := range c.dsts {
|
||||
if dst == nil {
|
||||
dststrs[i] = "(nil)"
|
||||
continue
|
||||
}
|
||||
dststrs[i] = fmt.Sprintf("%d", dst.id)
|
||||
if dst.screen {
|
||||
dststrs[i] += " (screen)"
|
||||
}
|
||||
}
|
||||
|
||||
var srcstrs [graphics.ShaderImageCount]string
|
||||
var srcstrs [graphics.ShaderSrcImageCount]string
|
||||
for i, src := range c.srcs {
|
||||
if src == nil {
|
||||
srcstrs[i] = "(nil)"
|
||||
@ -98,7 +105,7 @@ func (c *drawTrianglesCommand) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], num of dst regions: %d, num of indices: %d, blend: %s, fill rule: %s, shader id: %d", dst, strings.Join(srcstrs[:], ", "), len(c.dstRegions), c.numIndices(), blend, c.fillRule, c.shader.id)
|
||||
return fmt.Sprintf("draw-triangles: dst: [%s] <- src: [%s], num of dst regions: %d, num of indices: %d, blend: %s, fill rule: %s, shader id: %d", strings.Join(dststrs[:], ", "), strings.Join(srcstrs[:], ", "), len(c.dstRegions), c.numIndices(), blend, c.fillRule, c.shader.id)
|
||||
}
|
||||
|
||||
// Exec executes the drawTrianglesCommand.
|
||||
@ -108,16 +115,25 @@ func (c *drawTrianglesCommand) Exec(commandQueue *commandQueue, graphicsDriver g
|
||||
return nil
|
||||
}
|
||||
|
||||
var imgs [graphics.ShaderImageCount]graphicsdriver.ImageID
|
||||
for i, src := range c.srcs {
|
||||
if src == nil {
|
||||
imgs[i] = graphicsdriver.InvalidImageID
|
||||
var dsts [graphics.ShaderDstImageCount]graphicsdriver.ImageID
|
||||
for i, dst := range c.dsts {
|
||||
if dst == nil {
|
||||
dsts[i] = graphicsdriver.InvalidImageID
|
||||
continue
|
||||
}
|
||||
imgs[i] = src.image.ID()
|
||||
dsts[i] = dst.image.ID()
|
||||
}
|
||||
|
||||
return graphicsDriver.DrawTriangles(c.dst.image.ID(), imgs, c.shader.shader.ID(), c.dstRegions, indexOffset, c.blend, c.uniforms, c.fillRule)
|
||||
var srcs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID
|
||||
for i, src := range c.srcs {
|
||||
if src == nil {
|
||||
srcs[i] = graphicsdriver.InvalidImageID
|
||||
continue
|
||||
}
|
||||
srcs[i] = src.image.ID()
|
||||
}
|
||||
|
||||
return graphicsDriver.DrawTriangles(dsts, srcs, c.shader.shader.ID(), c.dstRegions, indexOffset, c.blend, c.uniforms, c.fillRule)
|
||||
}
|
||||
|
||||
func (c *drawTrianglesCommand) NeedsSync() bool {
|
||||
@ -142,7 +158,7 @@ func (c *drawTrianglesCommand) setVertices(vertices []float32) {
|
||||
|
||||
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
|
||||
// with the drawTrianglesCommand c.
|
||||
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, vertices []float32, blend graphicsdriver.Blend, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) bool {
|
||||
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, blend graphicsdriver.Blend, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) bool {
|
||||
if c.shader != shader {
|
||||
return false
|
||||
}
|
||||
@ -154,7 +170,7 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
|
||||
return false
|
||||
}
|
||||
}
|
||||
if c.dst != dst {
|
||||
if c.dsts != dsts {
|
||||
return false
|
||||
}
|
||||
if c.srcs != srcs {
|
||||
@ -166,7 +182,7 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
|
||||
if c.fillRule != fillRule {
|
||||
return false
|
||||
}
|
||||
if c.fillRule != graphicsdriver.FillAll && mightOverlapDstRegions(c.vertices, vertices) {
|
||||
if c.fillRule != graphicsdriver.FillRuleFillAll && mightOverlapDstRegions(c.vertices, vertices) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -43,14 +43,14 @@ const (
|
||||
maxVertexFloatCount = MaxVertexCount * graphics.VertexFloatCount
|
||||
)
|
||||
|
||||
var vsyncEnabled int32 = 1
|
||||
var vsyncEnabled atomic.Bool
|
||||
|
||||
func init() {
|
||||
vsyncEnabled.Store(true)
|
||||
}
|
||||
|
||||
func SetVsyncEnabled(enabled bool, graphicsDriver graphicsdriver.Graphics) {
|
||||
if enabled {
|
||||
atomic.StoreInt32(&vsyncEnabled, 1)
|
||||
} else {
|
||||
atomic.StoreInt32(&vsyncEnabled, 0)
|
||||
}
|
||||
vsyncEnabled.Store(enabled)
|
||||
|
||||
runOnRenderThread(func() {
|
||||
graphicsDriver.SetVsyncEnabled(enabled)
|
||||
@ -105,7 +105,7 @@ func mustUseDifferentVertexBuffer(nextNumVertexFloats int) bool {
|
||||
}
|
||||
|
||||
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
|
||||
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
func (q *commandQueue) EnqueueDrawTrianglesCommand(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
if len(vertices) > maxVertexFloatCount {
|
||||
panic(fmt.Sprintf("graphicscommand: len(vertices) must equal to or less than %d but was %d", maxVertexFloatCount, len(vertices)))
|
||||
}
|
||||
@ -125,7 +125,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
|
||||
// prependPreservedUniforms not only prepends values to the given slice but also creates a new slice.
|
||||
// Allocating a new slice is necessary to make EnqueueDrawTrianglesCommand safe so far.
|
||||
// TODO: This might cause a performance issue (#2601).
|
||||
uniforms = q.prependPreservedUniforms(uniforms, shader, dst, srcs, dstRegion, srcRegions)
|
||||
uniforms = q.prependPreservedUniforms(uniforms, shader, dsts, srcs, dstRegion, srcRegions)
|
||||
|
||||
// Remove unused uniform variables so that more commands can be merged.
|
||||
shader.ir.FilterUniformVariables(uniforms)
|
||||
@ -133,7 +133,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
|
||||
// TODO: If dst is the screen, reorder the command to be the last.
|
||||
if !split && 0 < len(q.commands) {
|
||||
if last, ok := q.commands[len(q.commands)-1].(*drawTrianglesCommand); ok {
|
||||
if last.CanMergeWithDrawTrianglesCommand(dst, srcs, vertices, blend, shader, uniforms, fillRule) {
|
||||
if last.CanMergeWithDrawTrianglesCommand(dsts, srcs, vertices, blend, shader, uniforms, fillRule) {
|
||||
last.setVertices(q.lastVertices(len(vertices) + last.numVertices()))
|
||||
if last.dstRegions[len(last.dstRegions)-1].Region == dstRegion {
|
||||
last.dstRegions[len(last.dstRegions)-1].IndexCount += len(indices)
|
||||
@ -149,7 +149,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
|
||||
}
|
||||
|
||||
c := q.drawTrianglesCommandPool.get()
|
||||
c.dst = dst
|
||||
c.dsts = dsts
|
||||
c.srcs = srcs
|
||||
c.vertices = q.lastVertices(len(vertices))
|
||||
c.blend = blend
|
||||
@ -185,7 +185,7 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo
|
||||
|
||||
var sync bool
|
||||
// Disable asynchronous rendering when vsync is on, as this causes a rendering delay (#2822).
|
||||
if endFrame && atomic.LoadInt32(&vsyncEnabled) != 0 {
|
||||
if endFrame && vsyncEnabled.Load() {
|
||||
sync = true
|
||||
}
|
||||
if !sync {
|
||||
@ -324,18 +324,25 @@ func imageRectangleToRectangleF32(r image.Rectangle) rectangleF32 {
|
||||
}
|
||||
}
|
||||
|
||||
func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shader, dst *Image, srcs [graphics.ShaderImageCount]*Image, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle) []uint32 {
|
||||
func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shader, dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.ShaderSrcImageCount]*Image, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle) []uint32 {
|
||||
origUniforms := uniforms
|
||||
uniforms = q.uint32sBuffer.alloc(len(origUniforms) + graphics.PreservedUniformUint32Count)
|
||||
copy(uniforms[graphics.PreservedUniformUint32Count:], origUniforms)
|
||||
|
||||
// Set the destination texture size.
|
||||
dw, dh := dst.InternalSize()
|
||||
var firstDst *Image
|
||||
for _, dst := range dsts {
|
||||
if dst != nil {
|
||||
firstDst = dst
|
||||
break
|
||||
}
|
||||
}
|
||||
dw, dh := firstDst.InternalSize()
|
||||
uniforms[0] = math.Float32bits(float32(dw))
|
||||
uniforms[1] = math.Float32bits(float32(dh))
|
||||
uniformIndex := 2
|
||||
|
||||
for i := 0; i < graphics.ShaderImageCount; i++ {
|
||||
for i := 0; i < graphics.ShaderSrcImageCount; i++ {
|
||||
var floatW, floatH uint32
|
||||
if srcs[i] != nil {
|
||||
w, h := srcs[i].InternalSize()
|
||||
@ -346,7 +353,7 @@ func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shade
|
||||
uniforms[uniformIndex+i*2] = floatW
|
||||
uniforms[uniformIndex+1+i*2] = floatH
|
||||
}
|
||||
uniformIndex += graphics.ShaderImageCount * 2
|
||||
uniformIndex += graphics.ShaderSrcImageCount * 2
|
||||
|
||||
dr := imageRectangleToRectangleF32(dstRegion)
|
||||
if shader.unit() == shaderir.Texels {
|
||||
@ -366,7 +373,7 @@ func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shade
|
||||
uniforms[uniformIndex+1] = math.Float32bits(dr.height)
|
||||
uniformIndex += 2
|
||||
|
||||
var srs [graphics.ShaderImageCount]rectangleF32
|
||||
var srs [graphics.ShaderSrcImageCount]rectangleF32
|
||||
for i, r := range srcRegions {
|
||||
srs[i] = imageRectangleToRectangleF32(r)
|
||||
}
|
||||
@ -384,18 +391,18 @@ func (q *commandQueue) prependPreservedUniforms(uniforms []uint32, shader *Shade
|
||||
}
|
||||
|
||||
// Set the source region origins.
|
||||
for i := 0; i < graphics.ShaderImageCount; i++ {
|
||||
for i := 0; i < graphics.ShaderSrcImageCount; i++ {
|
||||
uniforms[uniformIndex+i*2] = math.Float32bits(srs[i].x)
|
||||
uniforms[uniformIndex+1+i*2] = math.Float32bits(srs[i].y)
|
||||
}
|
||||
uniformIndex += graphics.ShaderImageCount * 2
|
||||
uniformIndex += graphics.ShaderSrcImageCount * 2
|
||||
|
||||
// Set the source region sizes.
|
||||
for i := 0; i < graphics.ShaderImageCount; i++ {
|
||||
for i := 0; i < graphics.ShaderSrcImageCount; i++ {
|
||||
uniforms[uniformIndex+i*2] = math.Float32bits(srs[i].width)
|
||||
uniforms[uniformIndex+1+i*2] = math.Float32bits(srs[i].height)
|
||||
}
|
||||
uniformIndex += graphics.ShaderImageCount * 2
|
||||
uniformIndex += graphics.ShaderSrcImageCount * 2
|
||||
|
||||
// Set the projection matrix.
|
||||
uniforms[uniformIndex] = math.Float32bits(2 / float32(dw))
|
||||
@ -469,11 +476,11 @@ func (c *commandQueueManager) putCommandQueue(commandQueue *commandQueue) {
|
||||
c.pool.put(commandQueue)
|
||||
}
|
||||
|
||||
func (c *commandQueueManager) enqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
func (c *commandQueueManager) enqueueDrawTrianglesCommand(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
if c.current == nil {
|
||||
c.current, _ = c.pool.get()
|
||||
}
|
||||
c.current.EnqueueDrawTrianglesCommand(dst, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
c.current.EnqueueDrawTrianglesCommand(dsts, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
}
|
||||
|
||||
func (c *commandQueueManager) flush(graphicsDriver graphicsdriver.Graphics, endFrame bool) error {
|
||||
|
@ -130,7 +130,7 @@ func (i *Image) InternalSize() (int, int) {
|
||||
//
|
||||
// If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the
|
||||
// elements for the source image are not used.
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
for _, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
@ -142,7 +142,48 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
}
|
||||
i.flushBufferedWritePixels()
|
||||
|
||||
theCommandQueueManager.enqueueDrawTrianglesCommand(i, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
theCommandQueueManager.enqueueDrawTrianglesCommand([graphics.ShaderDstImageCount]*Image{i}, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
}
|
||||
|
||||
// DrawTriangles draws triangles with the given image.
|
||||
//
|
||||
// The vertex floats are:
|
||||
//
|
||||
// 0: Destination X in pixels
|
||||
// 1: Destination Y in pixels
|
||||
// 2: Source X in texels
|
||||
// 3: Source Y in texels
|
||||
// 4: Color R [0.0-1.0]
|
||||
// 5: Color G
|
||||
// 6: Color B
|
||||
// 7: Color Y
|
||||
//
|
||||
// src and shader are exclusive and only either is non-nil.
|
||||
//
|
||||
// The elements that index is in between 2 and 7 are used for the source images.
|
||||
// The source image is 1) src argument if non-nil, or 2) an image value in the uniform variables if it exists.
|
||||
// If there are multiple images in the uniform variables, the smallest ID's value is adopted.
|
||||
//
|
||||
// If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the
|
||||
// elements for the source image are not used.
|
||||
func DrawTrianglesMRT(dsts [graphics.ShaderDstImageCount]*Image, srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule) {
|
||||
for _, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
if src.screen {
|
||||
panic("graphicscommand: the screen image cannot be the rendering des")
|
||||
}
|
||||
src.flushBufferedWritePixels()
|
||||
}
|
||||
for _, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
dst.flushBufferedWritePixels()
|
||||
}
|
||||
|
||||
theCommandQueueManager.enqueueDrawTrianglesCommand(dsts, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
}
|
||||
|
||||
// ReadPixels reads the image's pixels.
|
||||
|
@ -59,7 +59,7 @@ func TestClear(t *testing.T) {
|
||||
vs := quadVertices(w/2, h/2)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
pix := make([]byte, 4*w*h)
|
||||
if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), []graphicsdriver.PixelsArgs{
|
||||
@ -90,8 +90,8 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) {
|
||||
vs := quadVertices(w/2, h/2)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
bs := graphics.NewManagedBytes(4, func(bs []byte) {
|
||||
for i := range bs {
|
||||
bs[i] = 0
|
||||
@ -109,11 +109,11 @@ func TestShader(t *testing.T) {
|
||||
vs := quadVertices(w, h)
|
||||
is := graphics.QuadIndices()
|
||||
dr := image.Rect(0, 0, w, h)
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
g := ui.Get().GraphicsDriverForTesting()
|
||||
s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff))
|
||||
dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillAll)
|
||||
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll)
|
||||
|
||||
pix := make([]byte, 4*w*h)
|
||||
if err := dst.ReadPixels(g, []graphicsdriver.PixelsArgs{
|
||||
|
@ -71,7 +71,8 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
procD3DCompile *windows.LazyProc
|
||||
procD3DCompile *windows.LazyProc
|
||||
procD3DCreateBlob *windows.LazyProc
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -93,6 +94,7 @@ func init() {
|
||||
}
|
||||
|
||||
procD3DCompile = d3dcompiler.NewProc("D3DCompile")
|
||||
procD3DCreateBlob = d3dcompiler.NewProc("D3DCreateBlob")
|
||||
}
|
||||
|
||||
func isD3DCompilerDLLAvailable() bool {
|
||||
@ -135,6 +137,19 @@ func _D3DCompile(srcData []byte, sourceName string, pDefines []_D3D_SHADER_MACRO
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func _D3DCreateBlob(size uint) (*_ID3DBlob, error) {
|
||||
if !isD3DCompilerDLLAvailable() {
|
||||
return nil, fmt.Errorf("directx: d3dcompiler_*.dll is missing in this environment")
|
||||
}
|
||||
|
||||
var blob *_ID3DBlob
|
||||
r, _, _ := procD3DCreateBlob.Call(uintptr(size), uintptr(unsafe.Pointer(&blob)))
|
||||
if uint32(r) != uint32(windows.S_OK) {
|
||||
return nil, fmt.Errorf("directx: D3DCreateBlob failed: %w", handleError(windows.Handle(uint32(r))))
|
||||
}
|
||||
return blob, nil
|
||||
}
|
||||
|
||||
type _D3D_SHADER_MACRO struct {
|
||||
Name *byte
|
||||
Definition *byte
|
||||
|
@ -482,8 +482,7 @@ func (g *graphics11) MaxImageSize() int {
|
||||
}
|
||||
|
||||
func (g *graphics11) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) {
|
||||
vs, ps, offsets := hlsl.Compile(program)
|
||||
vsh, psh, err := compileShader(vs, ps)
|
||||
vsh, psh, err := compileShader(program)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -492,7 +491,7 @@ func (g *graphics11) NewShader(program *shaderir.Program) (graphicsdriver.Shader
|
||||
graphics: g,
|
||||
id: g.genNextShaderID(),
|
||||
uniformTypes: program.Uniforms,
|
||||
uniformOffsets: offsets,
|
||||
uniformOffsets: hlsl.CalcUniformMemoryOffsets(program),
|
||||
vertexShaderBlob: vsh,
|
||||
pixelShaderBlob: psh,
|
||||
}
|
||||
@ -515,14 +514,103 @@ func (g *graphics11) removeShader(s *shader11) {
|
||||
delete(g.shaders, s.id)
|
||||
}
|
||||
|
||||
func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
func (g *graphics11) setAsRenderTargets(dsts []*image11, useStencil bool) error {
|
||||
var rtvs []*_ID3D11RenderTargetView
|
||||
var dsv *_ID3D11DepthStencilView
|
||||
for _, dst := range dsts {
|
||||
// Ignore a nil image in case of MRT
|
||||
if dst == nil {
|
||||
rtvs = append(rtvs, nil)
|
||||
continue
|
||||
}
|
||||
if dst.renderTargetView == nil {
|
||||
rtv, err := g.device.CreateRenderTargetView(unsafe.Pointer(dst.texture), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.renderTargetView = rtv
|
||||
}
|
||||
rtvs = append(rtvs, dst.renderTargetView)
|
||||
|
||||
if !useStencil || dsv != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if dst.screen {
|
||||
return fmt.Errorf("directx: a stencil buffer is not available for a screen image")
|
||||
}
|
||||
if dst.stencil == nil {
|
||||
w, h := dst.internalSize()
|
||||
s, err := g.device.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{
|
||||
Width: uint32(w),
|
||||
Height: uint32(h),
|
||||
MipLevels: 0,
|
||||
ArraySize: 1,
|
||||
Format: _DXGI_FORMAT_D24_UNORM_S8_UINT,
|
||||
SampleDesc: _DXGI_SAMPLE_DESC{
|
||||
Count: 1,
|
||||
Quality: 0,
|
||||
},
|
||||
Usage: _D3D11_USAGE_DEFAULT,
|
||||
BindFlags: uint32(_D3D11_BIND_DEPTH_STENCIL),
|
||||
CPUAccessFlags: 0,
|
||||
MiscFlags: 0,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.stencil = s
|
||||
}
|
||||
if dst.stencilView == nil {
|
||||
sv, err := g.device.CreateDepthStencilView(unsafe.Pointer(dst.stencil), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.stencilView = sv
|
||||
}
|
||||
dsv = dst.stencilView
|
||||
}
|
||||
|
||||
g.deviceContext.OMSetRenderTargets(rtvs, dsv)
|
||||
if useStencil {
|
||||
g.deviceContext.ClearDepthStencilView(dsv, uint8(_D3D11_CLEAR_STENCIL), 0, 0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *graphics11) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdriver.ImageID, srcIDs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
// Remove bound textures first. This is needed to avoid warnings on the debugger.
|
||||
g.deviceContext.OMSetRenderTargets([]*_ID3D11RenderTargetView{nil}, nil)
|
||||
srvs := [graphics.ShaderImageCount]*_ID3D11ShaderResourceView{}
|
||||
srvs := [graphics.ShaderSrcImageCount]*_ID3D11ShaderResourceView{}
|
||||
g.deviceContext.PSSetShaderResources(0, srvs[:])
|
||||
|
||||
dst := g.images[dstID]
|
||||
var srcs [graphics.ShaderImageCount]*image11
|
||||
var dsts [graphics.ShaderDstImageCount]*image11
|
||||
var vp _D3D11_VIEWPORT
|
||||
var targetCount int
|
||||
firstTarget := -1
|
||||
for i, id := range dstIDs {
|
||||
img := g.images[id]
|
||||
if img == nil {
|
||||
continue
|
||||
}
|
||||
if firstTarget == -1 {
|
||||
firstTarget = i
|
||||
}
|
||||
dsts[i] = img
|
||||
w, h := img.internalSize()
|
||||
vp = _D3D11_VIEWPORT{
|
||||
TopLeftX: 0,
|
||||
TopLeftY: 0,
|
||||
Width: float32(w),
|
||||
Height: float32(h),
|
||||
MinDepth: 0,
|
||||
MaxDepth: 1,
|
||||
}
|
||||
targetCount++
|
||||
}
|
||||
|
||||
var srcs [graphics.ShaderSrcImageCount]*image11
|
||||
for i, id := range srcIDs {
|
||||
img := g.images[id]
|
||||
if img == nil {
|
||||
@ -531,19 +619,16 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic
|
||||
srcs[i] = img
|
||||
}
|
||||
|
||||
w, h := dst.internalSize()
|
||||
g.deviceContext.RSSetViewports([]_D3D11_VIEWPORT{
|
||||
{
|
||||
TopLeftX: 0,
|
||||
TopLeftY: 0,
|
||||
Width: float32(w),
|
||||
Height: float32(h),
|
||||
MinDepth: 0,
|
||||
MaxDepth: 1,
|
||||
},
|
||||
})
|
||||
// If the number of targets is more than one, or if the only target is the first one, then
|
||||
// it is safe to assume that MRT is used.
|
||||
// Also, it only matters in order to specify empty targets/viewports when not all slots are
|
||||
// being filled, even though it's not a MRT scenario.
|
||||
if targetCount > 1 || firstTarget > 0 {
|
||||
targetCount = graphics.ShaderDstImageCount
|
||||
}
|
||||
|
||||
if err := dst.setAsRenderTarget(fillRule != graphicsdriver.FillAll); err != nil {
|
||||
g.deviceContext.RSSetViewports([]_D3D11_VIEWPORT{vp})
|
||||
if err := g.setAsRenderTargets(dsts[:targetCount], fillRule != graphicsdriver.FillRuleFillAll); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -553,7 +638,7 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic
|
||||
return err
|
||||
}
|
||||
|
||||
if fillRule == graphicsdriver.FillAll {
|
||||
if fillRule == graphicsdriver.FillRuleFillAll {
|
||||
bs, err := g.blendState(blend, noStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -578,9 +663,9 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic
|
||||
})
|
||||
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
case graphicsdriver.FillRuleFillAll:
|
||||
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
||||
case graphicsdriver.NonZero:
|
||||
case graphicsdriver.FillRuleNonZero:
|
||||
bs, err := g.blendState(blend, incrementStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -592,7 +677,7 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic
|
||||
}
|
||||
g.deviceContext.OMSetDepthStencilState(dss, 0)
|
||||
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
||||
case graphicsdriver.EvenOdd:
|
||||
case graphicsdriver.FillRuleEvenOdd:
|
||||
bs, err := g.blendState(blend, invertStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -606,7 +691,7 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic
|
||||
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
||||
}
|
||||
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
bs, err := g.blendState(blend, drawWithStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -40,13 +40,14 @@ func (r *resourceWithSize) release() {
|
||||
}
|
||||
|
||||
type graphics12 struct {
|
||||
debug *_ID3D12Debug
|
||||
device *_ID3D12Device
|
||||
commandQueue *_ID3D12CommandQueue
|
||||
rtvDescriptorHeap *_ID3D12DescriptorHeap
|
||||
rtvDescriptorSize uint32
|
||||
renderTargets [frameCount]*_ID3D12Resource
|
||||
framePipelineToken _D3D12XBOX_FRAME_PIPELINE_TOKEN
|
||||
debug *_ID3D12Debug
|
||||
device *_ID3D12Device
|
||||
commandQueue *_ID3D12CommandQueue
|
||||
rtvDescriptorHeap *_ID3D12DescriptorHeap
|
||||
rtvEmptyDescriptorHeap *_ID3D12DescriptorHeap
|
||||
rtvDescriptorSize uint32
|
||||
renderTargets [frameCount]*_ID3D12Resource
|
||||
framePipelineToken _D3D12XBOX_FRAME_PIPELINE_TOKEN
|
||||
|
||||
fence *_ID3D12Fence
|
||||
fenceValues [frameCount]uint64
|
||||
@ -418,6 +419,34 @@ func (g *graphics12) initializeMembers(frameIndex int) (ferr error) {
|
||||
g.rtvDescriptorHeap = nil
|
||||
}
|
||||
}()
|
||||
|
||||
// Create a descriptor heap for empty RTV in case of MRT with empty locations.
|
||||
h, err = g.device.CreateDescriptorHeap(&_D3D12_DESCRIPTOR_HEAP_DESC{
|
||||
Type: _D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
|
||||
NumDescriptors: frameCount,
|
||||
Flags: _D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
|
||||
NodeMask: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.rtvEmptyDescriptorHeap = h
|
||||
defer func() {
|
||||
if ferr != nil {
|
||||
g.rtvEmptyDescriptorHeap.Release()
|
||||
g.rtvEmptyDescriptorHeap = nil
|
||||
}
|
||||
}()
|
||||
hnd, err := g.rtvEmptyDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Create an empty render target for empty destinations at DrawTriangles
|
||||
g.device.CreateRenderTargetView(nil, &_D3D12_RENDER_TARGET_VIEW_DESC{
|
||||
Format: _DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
ViewDimension: _D3D12_RTV_DIMENSION_TEXTURE2D,
|
||||
}, hnd)
|
||||
|
||||
g.rtvDescriptorSize = g.device.GetDescriptorHandleIncrementSize(_D3D12_DESCRIPTOR_HEAP_TYPE_RTV)
|
||||
|
||||
if err := g.pipelineStates.initialize(g.device); err != nil {
|
||||
@ -1064,8 +1093,7 @@ func (g *graphics12) MaxImageSize() int {
|
||||
}
|
||||
|
||||
func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) {
|
||||
vs, ps, offsets := hlsl.Compile(program)
|
||||
vsh, psh, err := compileShader(vs, ps)
|
||||
vsh, psh, err := compileShader(program)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1074,7 +1102,7 @@ func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader
|
||||
graphics: g,
|
||||
id: g.genNextShaderID(),
|
||||
uniformTypes: program.Uniforms,
|
||||
uniformOffsets: offsets,
|
||||
uniformOffsets: hlsl.CalcUniformMemoryOffsets(program),
|
||||
vertexShader: vsh,
|
||||
pixelShader: psh,
|
||||
}
|
||||
@ -1082,7 +1110,82 @@ func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
func (g *graphics12) setAsRenderTargets(dsts []*image12, useStencil bool) error {
|
||||
var rtvs []_D3D12_CPU_DESCRIPTOR_HANDLE
|
||||
var dsv *_D3D12_CPU_DESCRIPTOR_HANDLE
|
||||
|
||||
for i, img := range dsts {
|
||||
// Ignore a nil image in case of MRT
|
||||
if img == nil {
|
||||
_ = i
|
||||
rtv, err := g.rtvEmptyDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rtv.Offset(int32(g.frameIndex), g.rtvDescriptorSize)
|
||||
rtvs = append(rtvs, rtv)
|
||||
continue
|
||||
}
|
||||
|
||||
if img.screen {
|
||||
if useStencil {
|
||||
return fmt.Errorf("directx: stencils are not available on the screen framebuffer")
|
||||
}
|
||||
|
||||
rtvBase, err := g.rtvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rtv := rtvBase
|
||||
rtv.Offset(int32(g.frameIndex), g.rtvDescriptorSize)
|
||||
rtvs = append(rtvs, rtv)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := img.ensureRenderTargetView(g.device); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rtvBase, err := img.rtvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rtv := rtvBase
|
||||
rtvs = append(rtvs, rtv)
|
||||
|
||||
if !useStencil {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := img.ensureDepthStencilView(g.device); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dsv != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
sv, err := img.dsvDescriptorHeap.GetCPUDescriptorHandleForHeapStart()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dsv = &sv
|
||||
}
|
||||
|
||||
if !useStencil {
|
||||
g.drawCommandList.OMSetRenderTargets(rtvs, false, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
g.drawCommandList.OMSetStencilRef(0)
|
||||
g.drawCommandList.OMSetRenderTargets(rtvs, false, dsv)
|
||||
g.drawCommandList.ClearDepthStencilView(*dsv, _D3D12_CLEAR_FLAG_STENCIL, 0, 0, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *graphics12) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdriver.ImageID, srcIDs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
if shaderID == graphicsdriver.InvalidShaderID {
|
||||
return fmt.Errorf("directx: shader ID is invalid")
|
||||
}
|
||||
@ -1093,7 +1196,7 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.
|
||||
|
||||
// Release constant buffers when too many ones will be created.
|
||||
numPipelines := 1
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
numPipelines = 2
|
||||
}
|
||||
if len(g.pipelineStates.constantBuffers[g.frameIndex])+numPipelines > numDescriptorsPerFrame {
|
||||
@ -1103,20 +1206,46 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.
|
||||
g.pipelineStates.releaseConstantBuffers(g.frameIndex)
|
||||
}
|
||||
|
||||
dst := g.images[dstID]
|
||||
var resourceBarriers []_D3D12_RESOURCE_BARRIER_Transition
|
||||
if rb, ok := dst.transiteState(_D3D12_RESOURCE_STATE_RENDER_TARGET); ok {
|
||||
resourceBarriers = append(resourceBarriers, rb)
|
||||
}
|
||||
|
||||
var srcImages [graphics.ShaderImageCount]*image12
|
||||
for i, srcID := range srcs {
|
||||
src := g.images[srcID]
|
||||
if src == nil {
|
||||
var dsts [graphics.ShaderDstImageCount]*image12
|
||||
var vp _D3D12_VIEWPORT
|
||||
var targetCount int
|
||||
firstTarget := -1
|
||||
for i, id := range dstIDs {
|
||||
img := g.images[id]
|
||||
if img == nil {
|
||||
continue
|
||||
}
|
||||
srcImages[i] = src
|
||||
if rb, ok := src.transiteState(_D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); ok {
|
||||
if firstTarget == -1 {
|
||||
firstTarget = i
|
||||
}
|
||||
dsts[i] = img
|
||||
w, h := img.internalSize()
|
||||
vp = _D3D12_VIEWPORT{
|
||||
TopLeftX: 0,
|
||||
TopLeftY: 0,
|
||||
Width: float32(w),
|
||||
Height: float32(h),
|
||||
MinDepth: _D3D12_MIN_DEPTH,
|
||||
MaxDepth: _D3D12_MAX_DEPTH,
|
||||
}
|
||||
|
||||
if rb, ok := img.transiteState(_D3D12_RESOURCE_STATE_RENDER_TARGET); ok {
|
||||
resourceBarriers = append(resourceBarriers, rb)
|
||||
}
|
||||
|
||||
targetCount++
|
||||
}
|
||||
|
||||
var srcs [graphics.ShaderSrcImageCount]*image12
|
||||
for i, srcID := range srcIDs {
|
||||
img := g.images[srcID]
|
||||
if img == nil {
|
||||
continue
|
||||
}
|
||||
srcs[i] = img
|
||||
if rb, ok := img.transiteState(_D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); ok {
|
||||
resourceBarriers = append(resourceBarriers, rb)
|
||||
}
|
||||
}
|
||||
@ -1125,25 +1254,26 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.
|
||||
g.drawCommandList.ResourceBarrier(resourceBarriers)
|
||||
}
|
||||
|
||||
if err := dst.setAsRenderTarget(g.drawCommandList, g.device, fillRule != graphicsdriver.FillAll); err != nil {
|
||||
// If the number of targets is more than one, or if the only target is the first one, then
|
||||
// it is safe to assume that MRT is used.
|
||||
// Also, it only matters in order to specify empty targets/viewports when not all slots are
|
||||
// being filled, even though it's not a MRT scenario.
|
||||
usesMRT := targetCount > 1 || firstTarget > 0
|
||||
if usesMRT {
|
||||
targetCount = graphics.ShaderDstImageCount
|
||||
}
|
||||
|
||||
if err := g.setAsRenderTargets(dsts[:targetCount], fillRule != graphicsdriver.FillRuleFillAll); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
shader := g.shaders[shaderID]
|
||||
adjustedUniforms := adjustUniforms(shader.uniformTypes, shader.uniformOffsets, uniforms)
|
||||
|
||||
w, h := dst.internalSize()
|
||||
g.needFlushDrawCommandList = true
|
||||
g.drawCommandList.RSSetViewports([]_D3D12_VIEWPORT{
|
||||
{
|
||||
TopLeftX: 0,
|
||||
TopLeftY: 0,
|
||||
Width: float32(w),
|
||||
Height: float32(h),
|
||||
MinDepth: _D3D12_MIN_DEPTH,
|
||||
MaxDepth: _D3D12_MAX_DEPTH,
|
||||
},
|
||||
})
|
||||
|
||||
g.drawCommandList.RSSetViewports([]_D3D12_VIEWPORT{vp})
|
||||
|
||||
g.drawCommandList.IASetPrimitiveTopology(_D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST)
|
||||
g.drawCommandList.IASetVertexBuffers(0, []_D3D12_VERTEX_BUFFER_VIEW{
|
||||
{
|
||||
@ -1158,7 +1288,7 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.
|
||||
Format: _DXGI_FORMAT_R32_UINT,
|
||||
})
|
||||
|
||||
if err := g.pipelineStates.drawTriangles(g.device, g.drawCommandList, g.frameIndex, dst.screen, srcImages, shader, dstRegions, adjustedUniforms, blend, indexOffset, fillRule); err != nil {
|
||||
if err := g.pipelineStates.drawTriangles(g.device, g.drawCommandList, g.frameIndex, !usesMRT && dsts[firstTarget].screen, srcs, shader, dstRegions, adjustedUniforms, blend, indexOffset, fillRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ func (i *image12) disposeImpl() {
|
||||
|
||||
func (i *image12) ReadPixels(args []graphicsdriver.PixelsArgs) error {
|
||||
if i.screen {
|
||||
return errors.New("directx: Pixels cannot be called on the screen")
|
||||
return errors.New("directx: ReadPixels cannot be called on the screen")
|
||||
}
|
||||
|
||||
if err := i.graphics.flushCommandList(i.graphics.drawCommandList); err != nil {
|
||||
|
@ -128,7 +128,7 @@ type pipelineStates struct {
|
||||
constantBufferMaps [frameCount][]uintptr
|
||||
}
|
||||
|
||||
const numConstantBufferAndSourceTextures = 1 + graphics.ShaderImageCount
|
||||
const numConstantBufferAndSourceTextures = 1 + graphics.ShaderSrcImageCount
|
||||
|
||||
func (p *pipelineStates) initialize(device *_ID3D12Device) (ferr error) {
|
||||
// Create a CBV/SRV/UAV descriptor heap.
|
||||
@ -180,7 +180,7 @@ func (p *pipelineStates) initialize(device *_ID3D12Device) (ferr error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D12GraphicsCommandList, frameIndex int, screen bool, srcs [graphics.ShaderImageCount]*image12, shader *shader12, dstRegions []graphicsdriver.DstRegion, uniforms []uint32, blend graphicsdriver.Blend, indexOffset int, fillRule graphicsdriver.FillRule) error {
|
||||
func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D12GraphicsCommandList, frameIndex int, screen bool, srcs [graphics.ShaderSrcImageCount]*image12, shader *shader12, dstRegions []graphicsdriver.DstRegion, uniforms []uint32, blend graphicsdriver.Blend, indexOffset int, fillRule graphicsdriver.FillRule) error {
|
||||
idx := len(p.constantBuffers[frameIndex])
|
||||
if idx >= numDescriptorsPerFrame {
|
||||
return fmt.Errorf("directx: too many constant buffers")
|
||||
@ -289,7 +289,7 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D
|
||||
}
|
||||
commandList.SetGraphicsRootDescriptorTable(2, sh)
|
||||
|
||||
if fillRule == graphicsdriver.FillAll {
|
||||
if fillRule == graphicsdriver.FillRuleFillAll {
|
||||
s, err := shader.pipelineState(blend, noStencil, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -307,16 +307,16 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D
|
||||
},
|
||||
})
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
case graphicsdriver.FillRuleFillAll:
|
||||
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
|
||||
case graphicsdriver.NonZero:
|
||||
case graphicsdriver.FillRuleNonZero:
|
||||
s, err := shader.pipelineState(blend, incrementStencil, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commandList.SetPipelineState(s)
|
||||
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
|
||||
case graphicsdriver.EvenOdd:
|
||||
case graphicsdriver.FillRuleEvenOdd:
|
||||
s, err := shader.pipelineState(blend, invertStencil, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -325,7 +325,7 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D
|
||||
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
|
||||
}
|
||||
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
s, err := shader.pipelineState(blend, drawWithStencil, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -354,7 +354,7 @@ func (p *pipelineStates) ensureRootSignature(device *_ID3D12Device) (rootSignatu
|
||||
}
|
||||
srv := _D3D12_DESCRIPTOR_RANGE{
|
||||
RangeType: _D3D12_DESCRIPTOR_RANGE_TYPE_SRV, // t0
|
||||
NumDescriptors: graphics.ShaderImageCount,
|
||||
NumDescriptors: graphics.ShaderSrcImageCount,
|
||||
BaseShaderRegister: 0,
|
||||
RegisterSpace: 0,
|
||||
OffsetInDescriptorsFromTableStart: 1,
|
||||
@ -484,6 +484,18 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
|
||||
}
|
||||
|
||||
// Create a pipeline state.
|
||||
rtBlendDesc := _D3D12_RENDER_TARGET_BLEND_DESC{
|
||||
BlendEnable: 1,
|
||||
LogicOpEnable: 0,
|
||||
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
|
||||
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
|
||||
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
|
||||
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
|
||||
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
|
||||
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
|
||||
LogicOp: _D3D12_LOGIC_OP_NOOP,
|
||||
RenderTargetWriteMask: writeMask,
|
||||
}
|
||||
psoDesc := _D3D12_GRAPHICS_PIPELINE_STATE_DESC{
|
||||
pRootSignature: rootSignature,
|
||||
VS: _D3D12_SHADER_BYTECODE{
|
||||
@ -498,18 +510,8 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
|
||||
AlphaToCoverageEnable: 0,
|
||||
IndependentBlendEnable: 0,
|
||||
RenderTarget: [8]_D3D12_RENDER_TARGET_BLEND_DESC{
|
||||
{
|
||||
BlendEnable: 1,
|
||||
LogicOpEnable: 0,
|
||||
SrcBlend: blendFactorToBlend12(blend.BlendFactorSourceRGB, false),
|
||||
DestBlend: blendFactorToBlend12(blend.BlendFactorDestinationRGB, false),
|
||||
BlendOp: blendOperationToBlendOp12(blend.BlendOperationRGB),
|
||||
SrcBlendAlpha: blendFactorToBlend12(blend.BlendFactorSourceAlpha, true),
|
||||
DestBlendAlpha: blendFactorToBlend12(blend.BlendFactorDestinationAlpha, true),
|
||||
BlendOpAlpha: blendOperationToBlendOp12(blend.BlendOperationAlpha),
|
||||
LogicOp: _D3D12_LOGIC_OP_NOOP,
|
||||
RenderTargetWriteMask: writeMask,
|
||||
},
|
||||
rtBlendDesc, rtBlendDesc, rtBlendDesc, rtBlendDesc,
|
||||
rtBlendDesc, rtBlendDesc, rtBlendDesc, rtBlendDesc, // TODO: need to fill them all?
|
||||
},
|
||||
},
|
||||
SampleMask: math.MaxUint32,
|
||||
@ -532,9 +534,10 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
|
||||
NumElements: uint32(len(inputElementDescsForDX12)),
|
||||
},
|
||||
PrimitiveTopologyType: _D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||||
NumRenderTargets: 1,
|
||||
NumRenderTargets: graphics.ShaderDstImageCount,
|
||||
RTVFormats: [8]_DXGI_FORMAT{
|
||||
rtvFormat,
|
||||
rtvFormat, rtvFormat, rtvFormat, rtvFormat,
|
||||
rtvFormat, rtvFormat, rtvFormat, rtvFormat,
|
||||
},
|
||||
DSVFormat: dsvFormat,
|
||||
SampleDesc: _DXGI_SAMPLE_DESC{
|
||||
|
@ -78,7 +78,7 @@ func (s *shader11) disposeImpl() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *shader11) use(uniforms []uint32, srcs [graphics.ShaderImageCount]*image11) error {
|
||||
func (s *shader11) use(uniforms []uint32, srcs [graphics.ShaderSrcImageCount]*image11) error {
|
||||
vs, err := s.ensureVertexShader()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -114,7 +114,7 @@ func (s *shader11) use(uniforms []uint32, srcs [graphics.ShaderImageCount]*image
|
||||
s.graphics.deviceContext.Unmap(unsafe.Pointer(cb), 0)
|
||||
|
||||
// Set the render sources.
|
||||
var srvs [graphics.ShaderImageCount]*_ID3D11ShaderResourceView
|
||||
var srvs [graphics.ShaderSrcImageCount]*_ID3D11ShaderResourceView
|
||||
for i, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
|
@ -16,18 +16,67 @@ package directx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl"
|
||||
)
|
||||
|
||||
const (
|
||||
VertexShaderProfile = "vs_4_0"
|
||||
PixelShaderProfile = "ps_4_0"
|
||||
|
||||
VertexShaderEntryPoint = "VSMain"
|
||||
PixelShaderEntryPoint = "PSMain"
|
||||
)
|
||||
|
||||
type fxcPair struct {
|
||||
vertex []byte
|
||||
pixel []byte
|
||||
}
|
||||
|
||||
type precompiledFXCs struct {
|
||||
binaries map[shaderir.SourceHash]fxcPair
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func (c *precompiledFXCs) put(hash shaderir.SourceHash, vertex, pixel []byte) {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
if c.binaries == nil {
|
||||
c.binaries = map[shaderir.SourceHash]fxcPair{}
|
||||
}
|
||||
if _, ok := c.binaries[hash]; ok {
|
||||
panic(fmt.Sprintf("directx: the precompiled library for the hash %s is already registered", hash.String()))
|
||||
}
|
||||
c.binaries[hash] = fxcPair{
|
||||
vertex: vertex,
|
||||
pixel: pixel,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *precompiledFXCs) get(hash shaderir.SourceHash) ([]byte, []byte) {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
f := c.binaries[hash]
|
||||
return f.vertex, f.pixel
|
||||
}
|
||||
|
||||
var thePrecompiledFXCs precompiledFXCs
|
||||
|
||||
func RegisterPrecompiledFXCs(source []byte, vertex, pixel []byte) {
|
||||
thePrecompiledFXCs.put(shaderir.CalcSourceHash(source), vertex, pixel)
|
||||
}
|
||||
|
||||
var vertexShaderCache = map[string]*_ID3DBlob{}
|
||||
|
||||
func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) {
|
||||
var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3)
|
||||
|
||||
func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, ferr error) {
|
||||
defer func() {
|
||||
if ferr == nil {
|
||||
return
|
||||
@ -40,6 +89,22 @@ func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) {
|
||||
}
|
||||
}()
|
||||
|
||||
if vshBin, pshBin := thePrecompiledFXCs.get(program.SourceHash); vshBin != nil && pshBin != nil {
|
||||
var err error
|
||||
if vsh, err = _D3DCreateBlob(uint(len(vshBin))); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if psh, err = _D3DCreateBlob(uint(len(pshBin))); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
copy(unsafe.Slice((*byte)(vsh.GetBufferPointer()), vsh.GetBufferSize()), vshBin)
|
||||
copy(unsafe.Slice((*byte)(psh.GetBufferPointer()), psh.GetBufferSize()), pshBin)
|
||||
return vsh, psh, nil
|
||||
}
|
||||
|
||||
vs, ps := hlsl.Compile(program)
|
||||
var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3)
|
||||
|
||||
var wg errgroup.Group
|
||||
|
||||
// Vertex shaders are likely the same. If so, reuse the same _ID3DBlob.
|
||||
@ -56,7 +121,7 @@ func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) {
|
||||
}
|
||||
}()
|
||||
wg.Go(func() error {
|
||||
v, err := _D3DCompile([]byte(vs), "shader", nil, nil, "VSMain", "vs_4_0", flag, 0)
|
||||
v, err := _D3DCompile([]byte(vs), "shader", nil, nil, VertexShaderEntryPoint, VertexShaderProfile, flag, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("directx: D3DCompile for VSMain failed, original source: %s, %w", vs, err)
|
||||
}
|
||||
@ -65,7 +130,7 @@ func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) {
|
||||
})
|
||||
}
|
||||
wg.Go(func() error {
|
||||
p, err := _D3DCompile([]byte(ps), "shader", nil, nil, "PSMain", "ps_4_0", flag, 0)
|
||||
p, err := _D3DCompile([]byte(ps), "shader", nil, nil, PixelShaderEntryPoint, PixelShaderProfile, flag, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("directx: D3DCompile for PSMain failed, original source: %s, %w", ps, err)
|
||||
}
|
||||
@ -77,7 +142,7 @@ func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return
|
||||
return vsh, psh, nil
|
||||
}
|
||||
|
||||
func constantBufferSize(uniformTypes []shaderir.Type, uniformOffsets []int) int {
|
||||
|
@ -30,19 +30,19 @@ type DstRegion struct {
|
||||
type FillRule int
|
||||
|
||||
const (
|
||||
FillAll FillRule = iota
|
||||
NonZero
|
||||
EvenOdd
|
||||
FillRuleFillAll FillRule = iota
|
||||
FillRuleNonZero
|
||||
FillRuleEvenOdd
|
||||
)
|
||||
|
||||
func (f FillRule) String() string {
|
||||
switch f {
|
||||
case FillAll:
|
||||
return "FillAll"
|
||||
case NonZero:
|
||||
return "NonZero"
|
||||
case EvenOdd:
|
||||
return "EvenOdd"
|
||||
case FillRuleFillAll:
|
||||
return "FillRuleFillAll"
|
||||
case FillRuleNonZero:
|
||||
return "FillRuleNonZero"
|
||||
case FillRuleEvenOdd:
|
||||
return "FillRuleEvenOdd"
|
||||
default:
|
||||
return fmt.Sprintf("FillRule(%d)", f)
|
||||
}
|
||||
@ -68,7 +68,7 @@ type Graphics interface {
|
||||
NewShader(program *shaderir.Program) (Shader, error)
|
||||
|
||||
// DrawTriangles draws an image onto another image with the given parameters.
|
||||
DrawTriangles(dst ImageID, srcs [graphics.ShaderImageCount]ImageID, shader ShaderID, dstRegions []DstRegion, indexOffset int, blend Blend, uniforms []uint32, fillRule FillRule) error
|
||||
DrawTriangles(dsts [graphics.ShaderDstImageCount]ImageID, srcs [graphics.ShaderSrcImageCount]ImageID, shader ShaderID, dstRegions []DstRegion, indexOffset int, blend Blend, uniforms []uint32, fillRule FillRule) error
|
||||
}
|
||||
|
||||
type Resetter interface {
|
||||
|
@ -35,7 +35,7 @@ import (
|
||||
// Layer is an object that manages image-based content and
|
||||
// allows you to perform animations on that content.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/calayer.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/calayer?language=objc.
|
||||
type Layer interface {
|
||||
// Layer returns the underlying CALayer * pointer.
|
||||
Layer() unsafe.Pointer
|
||||
@ -43,15 +43,15 @@ type Layer interface {
|
||||
|
||||
// MetalLayer is a Core Animation Metal layer, a layer that manages a pool of Metal drawables.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc.
|
||||
type MetalLayer struct {
|
||||
metalLayer objc.ID
|
||||
}
|
||||
|
||||
// MakeMetalLayer creates a new Core Animation Metal layer.
|
||||
// NewMetalLayer creates a new Core Animation Metal layer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
|
||||
func MakeMetalLayer() (MetalLayer, error) {
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc.
|
||||
func NewMetalLayer() (MetalLayer, error) {
|
||||
coreGraphics, err := purego.Dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
return MetalLayer{}, err
|
||||
@ -88,14 +88,14 @@ func (ml MetalLayer) Layer() unsafe.Pointer {
|
||||
|
||||
// PixelFormat returns the pixel format of textures for rendering layer content.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat?language=objc.
|
||||
func (ml MetalLayer) PixelFormat() mtl.PixelFormat {
|
||||
return mtl.PixelFormat(ml.metalLayer.Send(objc.RegisterName("pixelFormat")))
|
||||
}
|
||||
|
||||
// SetDevice sets the Metal device responsible for the layer's drawable resources.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device?language=objc.
|
||||
func (ml MetalLayer) SetDevice(device mtl.Device) {
|
||||
ml.metalLayer.Send(objc.RegisterName("setDevice:"), uintptr(device.Device()))
|
||||
}
|
||||
@ -111,7 +111,7 @@ func (ml MetalLayer) SetOpaque(opaque bool) {
|
||||
// PixelFormatRGBA16Float, PixelFormatBGRA10XR, or PixelFormatBGRA10XRSRGB.
|
||||
// SetPixelFormat panics for other values.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat?language=objc.
|
||||
func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) {
|
||||
switch pf {
|
||||
case mtl.PixelFormatRGBA8UNorm, mtl.PixelFormatRGBA8UNormSRGB, mtl.PixelFormatBGRA8UNorm, mtl.PixelFormatBGRA8UNormSRGB, mtl.PixelFormatStencil8:
|
||||
@ -126,7 +126,7 @@ func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) {
|
||||
//
|
||||
// It can set to 2 or 3 only. SetMaximumDrawableCount panics for other values.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount?language=objc.
|
||||
func (ml MetalLayer) SetMaximumDrawableCount(count int) {
|
||||
if count < 2 || count > 3 {
|
||||
panic(errors.New(fmt.Sprintf("failed trying to set maximumDrawableCount to %d outside of the valid range of [2, 3]", count)))
|
||||
@ -137,7 +137,7 @@ func (ml MetalLayer) SetMaximumDrawableCount(count int) {
|
||||
// SetDisplaySyncEnabled controls whether the Metal layer and its drawables
|
||||
// are synchronized with the display's refresh rate.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled?language=objc.
|
||||
func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) {
|
||||
if runtime.GOOS == "ios" {
|
||||
return
|
||||
@ -147,7 +147,7 @@ func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) {
|
||||
|
||||
// SetDrawableSize sets the size, in pixels, of textures for rendering layer content.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize?language=objc.
|
||||
func (ml MetalLayer) SetDrawableSize(width, height int) {
|
||||
// TODO: once objc supports calling functions with struct arguments replace this with just a ID.Send call
|
||||
var sel_setDrawableSize = objc.RegisterName("setDrawableSize:")
|
||||
@ -161,7 +161,7 @@ func (ml MetalLayer) SetDrawableSize(width, height int) {
|
||||
|
||||
// NextDrawable returns a Metal drawable.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable?language=objc.
|
||||
func (ml MetalLayer) NextDrawable() (MetalDrawable, error) {
|
||||
md := ml.metalLayer.Send(objc.RegisterName("nextDrawable"))
|
||||
if md == 0 {
|
||||
@ -172,28 +172,28 @@ func (ml MetalLayer) NextDrawable() (MetalDrawable, error) {
|
||||
|
||||
// PresentsWithTransaction returns a Boolean value that determines whether the layer presents its content using a Core Animation transaction.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction?language=objc
|
||||
func (ml MetalLayer) PresentsWithTransaction() bool {
|
||||
return ml.metalLayer.Send(objc.RegisterName("presentsWithTransaction")) != 0
|
||||
}
|
||||
|
||||
// SetPresentsWithTransaction sets a Boolean value that determines whether the layer presents its content using a Core Animation transaction.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction?language=objc
|
||||
func (ml MetalLayer) SetPresentsWithTransaction(presentsWithTransaction bool) {
|
||||
ml.metalLayer.Send(objc.RegisterName("setPresentsWithTransaction:"), presentsWithTransaction)
|
||||
}
|
||||
|
||||
// SetFramebufferOnly sets a Boolean value that determines whether the layer’s textures are used only for rendering.
|
||||
//
|
||||
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478168-framebufferonly
|
||||
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478168-framebufferonly?language=objc
|
||||
func (ml MetalLayer) SetFramebufferOnly(framebufferOnly bool) {
|
||||
ml.metalLayer.Send(objc.RegisterName("setFramebufferOnly:"), framebufferOnly)
|
||||
}
|
||||
|
||||
// MetalDrawable is a displayable resource that can be rendered or written to by Metal.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable?language=objc.
|
||||
type MetalDrawable struct {
|
||||
metalDrawable objc.ID
|
||||
}
|
||||
@ -205,14 +205,14 @@ func (md MetalDrawable) Drawable() unsafe.Pointer {
|
||||
|
||||
// Texture returns a Metal texture object representing the drawable object's content.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture?language=objc.
|
||||
func (md MetalDrawable) Texture() mtl.Texture {
|
||||
return mtl.NewTexture(md.metalDrawable.Send(objc.RegisterName("texture")))
|
||||
}
|
||||
|
||||
// Present presents the drawable onscreen as soon as possible.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldrawable/1470284-present.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldrawable/1470284-present?language=objc.
|
||||
func (md MetalDrawable) Present() {
|
||||
md.metalDrawable.Send(objc.RegisterName("present"))
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ func (g *Graphics) gcBuffers() {
|
||||
|
||||
func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
g.cb = g.cq.MakeCommandBuffer()
|
||||
g.cb = g.cq.CommandBuffer()
|
||||
}
|
||||
|
||||
var newBuf mtl.Buffer
|
||||
@ -197,7 +197,7 @@ func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
||||
}
|
||||
|
||||
if newBuf == (mtl.Buffer{}) {
|
||||
newBuf = g.view.getMTLDevice().MakeBufferWithLength(pow2(length), resourceStorageMode)
|
||||
newBuf = g.view.getMTLDevice().NewBufferWithLength(pow2(length), resourceStorageMode)
|
||||
}
|
||||
|
||||
if g.buffers == nil {
|
||||
@ -286,7 +286,7 @@ func (g *Graphics) NewImage(width, height int) (graphicsdriver.Image, error) {
|
||||
StorageMode: storageMode,
|
||||
Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget,
|
||||
}
|
||||
t := g.view.getMTLDevice().MakeTexture(td)
|
||||
t := g.view.getMTLDevice().NewTextureWithDescriptor(td)
|
||||
i := &Image{
|
||||
id: g.genNextImageID(),
|
||||
graphics: g,
|
||||
@ -397,7 +397,7 @@ func (g *Graphics) Initialize() error {
|
||||
}
|
||||
|
||||
// The stencil reference value is always 0 (default).
|
||||
g.dsss[noStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[noStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@ -411,7 +411,7 @@ func (g *Graphics) Initialize() error {
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
g.dsss[incrementStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[incrementStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@ -425,7 +425,7 @@ func (g *Graphics) Initialize() error {
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
g.dsss[invertStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[invertStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@ -439,7 +439,7 @@ func (g *Graphics) Initialize() error {
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
g.dsss[drawWithStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[drawWithStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@ -454,7 +454,7 @@ func (g *Graphics) Initialize() error {
|
||||
},
|
||||
})
|
||||
|
||||
g.cq = g.view.getMTLDevice().MakeCommandQueue()
|
||||
g.cq = g.view.getMTLDevice().NewCommandQueue()
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -467,11 +467,11 @@ func (g *Graphics) flushRenderCommandEncoderIfNeeded() {
|
||||
g.lastDst = nil
|
||||
}
|
||||
|
||||
func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs [graphics.ShaderImageCount]*Image, indexOffset int, shader *Shader, uniforms [][]uint32, blend graphicsdriver.Blend, fillRule graphicsdriver.FillRule) error {
|
||||
func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs [graphics.ShaderSrcImageCount]*Image, indexOffset int, shader *Shader, uniforms [][]uint32, blend graphicsdriver.Blend, fillRule graphicsdriver.FillRule) error {
|
||||
// When preparing a stencil buffer, flush the current render command encoder
|
||||
// to make sure the stencil buffer is cleared when loading.
|
||||
// TODO: What about clearing the stencil buffer by vertices?
|
||||
if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillAll {
|
||||
if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillRuleFillAll {
|
||||
g.flushRenderCommandEncoderIfNeeded()
|
||||
}
|
||||
g.lastDst = dst
|
||||
@ -497,7 +497,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
rpd.ColorAttachments[0].Texture = t
|
||||
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{}
|
||||
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
dst.ensureStencil()
|
||||
rpd.StencilAttachment.LoadAction = mtl.LoadActionClear
|
||||
rpd.StencilAttachment.StoreAction = mtl.StoreActionDontCare
|
||||
@ -505,9 +505,9 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
}
|
||||
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
g.cb = g.cq.MakeCommandBuffer()
|
||||
g.cb = g.cq.CommandBuffer()
|
||||
}
|
||||
g.rce = g.cb.MakeRenderCommandEncoder(rpd)
|
||||
g.rce = g.cb.RenderCommandEncoderWithDescriptor(rpd)
|
||||
}
|
||||
|
||||
w, h := dst.internalSize()
|
||||
@ -544,26 +544,26 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
drawWithStencilRpss mtl.RenderPipelineState
|
||||
)
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
case graphicsdriver.FillRuleFillAll:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, noStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
noStencilRpss = s
|
||||
case graphicsdriver.NonZero:
|
||||
case graphicsdriver.FillRuleNonZero:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, incrementStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
incrementStencilRpss = s
|
||||
case graphicsdriver.EvenOdd:
|
||||
case graphicsdriver.FillRuleEvenOdd:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, invertStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
invertStencilRpss = s
|
||||
}
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -580,20 +580,20 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
})
|
||||
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
case graphicsdriver.FillRuleFillAll:
|
||||
g.rce.SetDepthStencilState(g.dsss[noStencil])
|
||||
g.rce.SetRenderPipelineState(noStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
case graphicsdriver.NonZero:
|
||||
case graphicsdriver.FillRuleNonZero:
|
||||
g.rce.SetDepthStencilState(g.dsss[incrementStencil])
|
||||
g.rce.SetRenderPipelineState(incrementStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
case graphicsdriver.EvenOdd:
|
||||
case graphicsdriver.FillRuleEvenOdd:
|
||||
g.rce.SetDepthStencilState(g.dsss[invertStencil])
|
||||
g.rce.SetRenderPipelineState(invertStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
}
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
g.rce.SetDepthStencilState(g.dsss[drawWithStencil])
|
||||
g.rce.SetRenderPipelineState(drawWithStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
@ -605,18 +605,18 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
func (g *Graphics) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdriver.ImageID, srcIDs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
if shaderID == graphicsdriver.InvalidShaderID {
|
||||
return fmt.Errorf("metal: shader ID is invalid")
|
||||
}
|
||||
|
||||
dst := g.images[dstID]
|
||||
dst := g.images[dstIDs[0]]
|
||||
|
||||
if dst.screen {
|
||||
g.view.update()
|
||||
}
|
||||
|
||||
var srcs [graphics.ShaderImageCount]*Image
|
||||
var srcs [graphics.ShaderSrcImageCount]*Image
|
||||
for i, srcID := range srcIDs {
|
||||
srcs[i] = g.images[srcID]
|
||||
}
|
||||
@ -810,8 +810,8 @@ func (i *Image) syncTexture() {
|
||||
panic("metal: command buffer must be empty at syncTexture: flushIfNeeded is not called yet?")
|
||||
}
|
||||
|
||||
cb := i.graphics.cq.MakeCommandBuffer()
|
||||
bce := cb.MakeBlitCommandEncoder()
|
||||
cb := i.graphics.cq.CommandBuffer()
|
||||
bce := cb.BlitCommandEncoder()
|
||||
bce.SynchronizeTexture(i.texture, 0, 0)
|
||||
bce.EndEncoding()
|
||||
|
||||
@ -859,7 +859,7 @@ func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error {
|
||||
StorageMode: storageMode,
|
||||
Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget,
|
||||
}
|
||||
t := g.view.getMTLDevice().MakeTexture(td)
|
||||
t := g.view.getMTLDevice().NewTextureWithDescriptor(td)
|
||||
g.tmpTextures = append(g.tmpTextures, t)
|
||||
|
||||
for _, a := range args {
|
||||
@ -870,9 +870,9 @@ func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error {
|
||||
}
|
||||
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
g.cb = i.graphics.cq.MakeCommandBuffer()
|
||||
g.cb = i.graphics.cq.CommandBuffer()
|
||||
}
|
||||
bce := g.cb.MakeBlitCommandEncoder()
|
||||
bce := g.cb.BlitCommandEncoder()
|
||||
for _, a := range args {
|
||||
so := mtl.Origin{X: a.Region.Min.X - region.Min.X, Y: a.Region.Min.Y - region.Min.Y, Z: 0}
|
||||
ss := mtl.Size{Width: a.Region.Dx(), Height: a.Region.Dy(), Depth: 1}
|
||||
@ -914,5 +914,5 @@ func (i *Image) ensureStencil() {
|
||||
StorageMode: mtl.StorageModePrivate,
|
||||
Usage: mtl.TextureUsageRenderTarget,
|
||||
}
|
||||
i.stencil = i.graphics.view.getMTLDevice().MakeTexture(td)
|
||||
i.stencil = i.graphics.view.getMTLDevice().NewTextureWithDescriptor(td)
|
||||
}
|
||||
|
39
internal/graphicsdriver/metal/mtl/dispatch_darwin.go
Normal file
39
internal/graphicsdriver/metal/mtl/dispatch_darwin.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2024 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package mtl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
var libSystem uintptr
|
||||
|
||||
var (
|
||||
dispatchDataCreate func(buffer unsafe.Pointer, size uint, queue uintptr, destructor uintptr) uintptr
|
||||
dispatchRelease func(obj uintptr)
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib, err := purego.Dlopen("/usr/lib/libSystem.B.dylib", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
libSystem = lib
|
||||
|
||||
purego.RegisterLibFunc(&dispatchDataCreate, libSystem, "dispatch_data_create")
|
||||
purego.RegisterLibFunc(&dispatchRelease, libSystem, "dispatch_release")
|
||||
}
|
@ -112,15 +112,15 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
return in.color;
|
||||
}
|
||||
`
|
||||
lib, err := device.MakeLibrary(source, mtl.CompileOptions{})
|
||||
lib, err := device.NewLibraryWithSource(source, mtl.CompileOptions{})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
vs, err := lib.MakeFunction("VertexShader")
|
||||
vs, err := lib.NewFunctionWithName("VertexShader")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fs, err := lib.MakeFunction("FragmentShader")
|
||||
fs, err := lib.NewFunctionWithName("FragmentShader")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
@ -129,7 +129,7 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
rpld.FragmentFunction = fs
|
||||
rpld.ColorAttachments[0].PixelFormat = mtl.PixelFormatRGBA8UNorm
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
|
||||
rps, err := device.MakeRenderPipelineState(rpld)
|
||||
rps, err := device.NewRenderPipelineStateWithDescriptor(rpld)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
@ -144,7 +144,7 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
{f32.Vec4{-0.75, -0.75, 0, 1}, f32.Vec4{1, 1, 1, 1}},
|
||||
{f32.Vec4{+0.75, -0.75, 0, 1}, f32.Vec4{0, 0, 0, 1}},
|
||||
}
|
||||
vertexBuffer := device.MakeBufferWithBytes(unsafe.Pointer(&vertexData[0]), unsafe.Sizeof(vertexData), mtl.ResourceStorageModeManaged)
|
||||
vertexBuffer := device.NewBufferWithBytes(unsafe.Pointer(&vertexData[0]), unsafe.Sizeof(vertexData), mtl.ResourceStorageModeManaged)
|
||||
|
||||
// Create an output texture to render into.
|
||||
td := mtl.TextureDescriptor{
|
||||
@ -154,10 +154,10 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
Height: 20,
|
||||
StorageMode: mtl.StorageModeManaged,
|
||||
}
|
||||
texture := device.MakeTexture(td)
|
||||
texture := device.NewTextureWithDescriptor(td)
|
||||
|
||||
cq := device.MakeCommandQueue()
|
||||
cb := cq.MakeCommandBuffer()
|
||||
cq := device.NewCommandQueue()
|
||||
cb := cq.CommandBuffer()
|
||||
|
||||
// Encode all render commands.
|
||||
var rpd mtl.RenderPassDescriptor
|
||||
@ -165,14 +165,14 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore
|
||||
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{Red: 0, Green: 0, Blue: 0, Alpha: 1}
|
||||
rpd.ColorAttachments[0].Texture = texture
|
||||
rce := cb.MakeRenderCommandEncoder(rpd)
|
||||
rce := cb.RenderCommandEncoderWithDescriptor(rpd)
|
||||
rce.SetRenderPipelineState(rps)
|
||||
rce.SetVertexBuffer(vertexBuffer, 0, 0)
|
||||
rce.DrawPrimitives(mtl.PrimitiveTypeTriangle, 0, 3)
|
||||
rce.EndEncoding()
|
||||
|
||||
// Encode all blit commands.
|
||||
bce := cb.MakeBlitCommandEncoder()
|
||||
bce := cb.BlitCommandEncoder()
|
||||
bce.Synchronize(texture)
|
||||
bce.EndEncoding()
|
||||
|
||||
|
@ -36,7 +36,7 @@ import (
|
||||
|
||||
// GPUFamily represents the functionality for families of GPUs.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlgpufamily
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlgpufamily?language=objc.
|
||||
type GPUFamily int
|
||||
|
||||
const (
|
||||
@ -54,7 +54,7 @@ const (
|
||||
|
||||
// FeatureSet defines a specific platform, hardware, and software configuration.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlfeatureset.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlfeatureset?language=objc.
|
||||
type FeatureSet uint16
|
||||
|
||||
const (
|
||||
@ -92,7 +92,7 @@ const (
|
||||
// TextureType defines The dimension of each image, including whether multiple images are arranged into an array or
|
||||
// a cube.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexturetype
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexturetype?language=objc.
|
||||
type TextureType uint16
|
||||
|
||||
const (
|
||||
@ -102,7 +102,7 @@ const (
|
||||
// PixelFormat defines data formats that describe the organization
|
||||
// and characteristics of individual pixels in a texture.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlpixelformat.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlpixelformat?language=objc.
|
||||
type PixelFormat uint16
|
||||
|
||||
// The data formats that describe the organization and characteristics
|
||||
@ -117,7 +117,7 @@ const (
|
||||
|
||||
// PrimitiveType defines geometric primitive types for drawing commands.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlprimitivetype.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlprimitivetype?language=objc.
|
||||
type PrimitiveType uint8
|
||||
|
||||
// Geometric primitive types for drawing commands.
|
||||
@ -132,7 +132,7 @@ const (
|
||||
// LoadAction defines actions performed at the start of a rendering pass
|
||||
// for a render command encoder.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlloadaction.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlloadaction?language=objc.
|
||||
type LoadAction uint8
|
||||
|
||||
// Actions performed at the start of a rendering pass for a render command encoder.
|
||||
@ -145,7 +145,7 @@ const (
|
||||
// StoreAction defines actions performed at the end of a rendering pass
|
||||
// for a render command encoder.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoreaction.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoreaction?language=objc.
|
||||
type StoreAction uint8
|
||||
|
||||
// Actions performed at the end of a rendering pass for a render command encoder.
|
||||
@ -160,7 +160,7 @@ const (
|
||||
|
||||
// StorageMode defines the memory location and access permissions of a resource.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode?language=objc.
|
||||
type StorageMode uint8
|
||||
|
||||
const (
|
||||
@ -189,7 +189,7 @@ const (
|
||||
// ResourceOptions defines optional arguments used to create
|
||||
// and influence behavior of buffer and texture objects.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlresourceoptions.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlresourceoptions?language=objc.
|
||||
type ResourceOptions uint16
|
||||
|
||||
const (
|
||||
@ -237,7 +237,7 @@ const (
|
||||
|
||||
// CPUCacheMode is the CPU cache mode that defines the CPU mapping of a resource.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcpucachemode.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcpucachemode?language=objc.
|
||||
type CPUCacheMode uint8
|
||||
|
||||
const (
|
||||
@ -252,7 +252,7 @@ const (
|
||||
|
||||
// IndexType is the index type for an index buffer that references vertices of geometric primitives.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode?language=objc
|
||||
type IndexType uint8
|
||||
|
||||
const (
|
||||
@ -358,7 +358,7 @@ const (
|
||||
// Resource represents a memory allocation for storing specialized data
|
||||
// that is accessible to the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlresource.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlresource?language=objc.
|
||||
type Resource interface {
|
||||
// resource returns the underlying id<MTLResource> pointer.
|
||||
resource() unsafe.Pointer
|
||||
@ -366,7 +366,7 @@ type Resource interface {
|
||||
|
||||
// RenderPipelineDescriptor configures new RenderPipelineState objects.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinedescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinedescriptor?language=objc.
|
||||
type RenderPipelineDescriptor struct {
|
||||
// VertexFunction is a programmable function that processes individual vertices in a rendering pass.
|
||||
VertexFunction Function
|
||||
@ -384,7 +384,7 @@ type RenderPipelineDescriptor struct {
|
||||
// RenderPipelineColorAttachmentDescriptor describes a color render target that specifies
|
||||
// the color configuration and color operations associated with a render pipeline.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinecolorattachmentdescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinecolorattachmentdescriptor?language=objc.
|
||||
type RenderPipelineColorAttachmentDescriptor struct {
|
||||
// PixelFormat is the pixel format of the color attachment's texture.
|
||||
PixelFormat PixelFormat
|
||||
@ -404,7 +404,7 @@ type RenderPipelineColorAttachmentDescriptor struct {
|
||||
// RenderPassDescriptor describes a group of render targets that serve as
|
||||
// the output destination for pixels generated by a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor?language=objc.
|
||||
type RenderPassDescriptor struct {
|
||||
// ColorAttachments is array of state information for attachments that store color data.
|
||||
ColorAttachments [1]RenderPassColorAttachmentDescriptor
|
||||
@ -416,7 +416,7 @@ type RenderPassDescriptor struct {
|
||||
// RenderPassColorAttachmentDescriptor describes a color render target that serves
|
||||
// as the output destination for color pixels generated by a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpasscolorattachmentdescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpasscolorattachmentdescriptor?language=objc.
|
||||
type RenderPassColorAttachmentDescriptor struct {
|
||||
RenderPassAttachmentDescriptor
|
||||
ClearColor ClearColor
|
||||
@ -425,7 +425,7 @@ type RenderPassColorAttachmentDescriptor struct {
|
||||
// RenderPassStencilAttachment describes a stencil render target that serves as the output
|
||||
// destination for stencil pixels generated by a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassstencilattachmentdescriptor
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassstencilattachmentdescriptor?language=objc.
|
||||
type RenderPassStencilAttachment struct {
|
||||
RenderPassAttachmentDescriptor
|
||||
}
|
||||
@ -433,7 +433,7 @@ type RenderPassStencilAttachment struct {
|
||||
// RenderPassAttachmentDescriptor describes a render target that serves
|
||||
// as the output destination for pixels generated by a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassattachmentdescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassattachmentdescriptor?language=objc.
|
||||
type RenderPassAttachmentDescriptor struct {
|
||||
LoadAction LoadAction
|
||||
StoreAction StoreAction
|
||||
@ -442,14 +442,14 @@ type RenderPassAttachmentDescriptor struct {
|
||||
|
||||
// ClearColor is an RGBA value used for a color pixel.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlclearcolor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlclearcolor?language=objc.
|
||||
type ClearColor struct {
|
||||
Red, Green, Blue, Alpha float64
|
||||
}
|
||||
|
||||
// TextureDescriptor configures new Texture objects.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexturedescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexturedescriptor?language=objc.
|
||||
type TextureDescriptor struct {
|
||||
TextureType TextureType
|
||||
PixelFormat PixelFormat
|
||||
@ -462,7 +462,7 @@ type TextureDescriptor struct {
|
||||
// Device is abstract representation of the GPU that
|
||||
// serves as the primary interface for a Metal app.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice?language=objc.
|
||||
type Device struct {
|
||||
device objc.ID
|
||||
|
||||
@ -493,6 +493,7 @@ var (
|
||||
sel_supportsFeatureSet = objc.RegisterName("supportsFeatureSet:")
|
||||
sel_newCommandQueue = objc.RegisterName("newCommandQueue")
|
||||
sel_newLibraryWithSource_options_error = objc.RegisterName("newLibraryWithSource:options:error:")
|
||||
sel_newLibraryWithData_error = objc.RegisterName("newLibraryWithData:error:")
|
||||
sel_release = objc.RegisterName("release")
|
||||
sel_retain = objc.RegisterName("retain")
|
||||
sel_new = objc.RegisterName("new")
|
||||
@ -567,7 +568,7 @@ var (
|
||||
|
||||
// CreateSystemDefaultDevice returns the preferred system default Metal device.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/1433401-mtlcreatesystemdefaultdevice.
|
||||
// Reference: https://developer.apple.com/documentation/metal/1433401-mtlcreatesystemdefaultdevice?language=objc.
|
||||
func CreateSystemDefaultDevice() (Device, error) {
|
||||
metal, err := purego.Dlopen("/System/Library/Frameworks/Metal.framework/Metal", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
@ -607,37 +608,36 @@ func (d Device) Device() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Point
|
||||
|
||||
// RespondsToSelector returns a Boolean value that indicates whether the receiver implements or inherits a method that can respond to a specified message.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector
|
||||
// Reference: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector?language=objc.
|
||||
func (d Device) RespondsToSelector(sel objc.SEL) bool {
|
||||
return d.device.Send(sel_respondsToSelector, sel) != 0
|
||||
}
|
||||
|
||||
// SupportsFamily returns a Boolean value that indicates whether the GPU device supports the feature set of a specific GPU family.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/3143473-supportsfamily
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/3143473-supportsfamily?language=objc.
|
||||
func (d Device) SupportsFamily(gpuFamily GPUFamily) bool {
|
||||
return d.device.Send(sel_supportsFamily, uintptr(gpuFamily)) != 0
|
||||
}
|
||||
|
||||
// SupportsFeatureSet reports whether device d supports feature set fs.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433418-supportsfeatureset.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433418-supportsfeatureset?language=objc.
|
||||
func (d Device) SupportsFeatureSet(fs FeatureSet) bool {
|
||||
return d.device.Send(sel_supportsFeatureSet, uintptr(fs)) != 0
|
||||
}
|
||||
|
||||
// MakeCommandQueue creates a serial command submission queue.
|
||||
// NewCommandQueue creates a queue you use to submit rendering and computation commands to a GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433388-makecommandqueue.
|
||||
func (d Device) MakeCommandQueue() CommandQueue {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433388-newcommandqueue?language=objc.
|
||||
func (d Device) NewCommandQueue() CommandQueue {
|
||||
return CommandQueue{d.device.Send(sel_newCommandQueue)}
|
||||
}
|
||||
|
||||
// MakeLibrary creates a new library that contains
|
||||
// the functions stored in the specified source string.
|
||||
// NewLibraryWithSource synchronously creates a Metal library instance by compiling the functions in a source string.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433431-makelibrary.
|
||||
func (d Device) MakeLibrary(source string, opt CompileOptions) (Library, error) {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433431-newlibrarywithsource?language=objc.
|
||||
func (d Device) NewLibraryWithSource(source string, opt CompileOptions) (Library, error) {
|
||||
var err cocoa.NSError
|
||||
l := d.device.Send(
|
||||
sel_newLibraryWithSource_options_error,
|
||||
@ -652,10 +652,31 @@ func (d Device) MakeLibrary(source string, opt CompileOptions) (Library, error)
|
||||
return Library{l}, nil
|
||||
}
|
||||
|
||||
// MakeRenderPipelineState creates a render pipeline state object.
|
||||
// NewLibraryWithData Creates a Metal library instance that contains the functions in a precompiled Metal library.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433369-makerenderpipelinestate.
|
||||
func (d Device) MakeRenderPipelineState(rpd RenderPipelineDescriptor) (RenderPipelineState, error) {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433391-newlibrarywithdata?language=objc.
|
||||
func (d Device) NewLibraryWithData(buffer []byte) (Library, error) {
|
||||
defer runtime.KeepAlive(buffer)
|
||||
|
||||
data := dispatchDataCreate(unsafe.Pointer(&buffer[0]), uint(len(buffer)), 0, 0)
|
||||
defer dispatchRelease(data)
|
||||
|
||||
var err cocoa.NSError
|
||||
l := d.device.Send(
|
||||
sel_newLibraryWithData_error,
|
||||
data,
|
||||
unsafe.Pointer(&err),
|
||||
)
|
||||
if l == 0 {
|
||||
return Library{}, errors.New(cocoa.NSString{ID: err.Send(sel_localizedDescription)}.String())
|
||||
}
|
||||
return Library{l}, nil
|
||||
}
|
||||
|
||||
// NewRenderPipelineStateWithDescriptor synchronously creates a render pipeline state.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433369-newrenderpipelinestatewithdescri?language=objc.
|
||||
func (d Device) NewRenderPipelineStateWithDescriptor(rpd RenderPipelineDescriptor) (RenderPipelineState, error) {
|
||||
renderPipelineDescriptor := objc.ID(class_MTLRenderPipelineDescriptor).Send(sel_new)
|
||||
renderPipelineDescriptor.Send(sel_setVertexFunction, rpd.VertexFunction.function)
|
||||
renderPipelineDescriptor.Send(sel_setFragmentFunction, rpd.FragmentFunction.function)
|
||||
@ -683,26 +704,24 @@ func (d Device) MakeRenderPipelineState(rpd RenderPipelineDescriptor) (RenderPip
|
||||
return RenderPipelineState{renderPipelineState}, nil
|
||||
}
|
||||
|
||||
// MakeBufferWithBytes allocates a new buffer of a given length
|
||||
// and initializes its contents by copying existing data into it.
|
||||
// NewBufferWithBytes allocates a new buffer of a given length and initializes its contents by copying existing data into it.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433429-makebuffer.
|
||||
func (d Device) MakeBufferWithBytes(bytes unsafe.Pointer, length uintptr, opt ResourceOptions) Buffer {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433429-newbufferwithbytes?language=objc.
|
||||
func (d Device) NewBufferWithBytes(bytes unsafe.Pointer, length uintptr, opt ResourceOptions) Buffer {
|
||||
return Buffer{d.device.Send(sel_newBufferWithBytes_length_options, bytes, length, uintptr(opt))}
|
||||
}
|
||||
|
||||
// MakeBufferWithLength allocates a new zero-filled buffer of a given length.
|
||||
// NewBufferWithLength allocates a new zero-filled buffer of a given length.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433375-newbufferwithlength
|
||||
func (d Device) MakeBufferWithLength(length uintptr, opt ResourceOptions) Buffer {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433375-newbufferwithlength?language=objc.
|
||||
func (d Device) NewBufferWithLength(length uintptr, opt ResourceOptions) Buffer {
|
||||
return Buffer{d.device.Send(sel_newBufferWithLength_options, length, uintptr(opt))}
|
||||
}
|
||||
|
||||
// MakeTexture creates a texture object with privately owned storage
|
||||
// that contains texture state.
|
||||
// NewTextureWithDescriptor creates a new texture instance.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433425-maketexture.
|
||||
func (d Device) MakeTexture(td TextureDescriptor) Texture {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433425-newtexturewithdescriptor?language=objc.
|
||||
func (d Device) NewTextureWithDescriptor(td TextureDescriptor) Texture {
|
||||
textureDescriptor := objc.ID(class_MTLTextureDescriptor).Send(sel_new)
|
||||
textureDescriptor.Send(sel_setTextureType, uintptr(td.TextureType))
|
||||
textureDescriptor.Send(sel_setPixelFormat, uintptr(td.PixelFormat))
|
||||
@ -717,10 +736,10 @@ func (d Device) MakeTexture(td TextureDescriptor) Texture {
|
||||
}
|
||||
}
|
||||
|
||||
// MakeDepthStencilState creates a new object that contains depth and stencil test state.
|
||||
// NewDepthStencilStateWithDescriptor creates a depth-stencil state instance.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433412-makedepthstencilstate
|
||||
func (d Device) MakeDepthStencilState(dsd DepthStencilDescriptor) DepthStencilState {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433412-newdepthstencilstatewithdescript?language=objc.
|
||||
func (d Device) NewDepthStencilStateWithDescriptor(dsd DepthStencilDescriptor) DepthStencilState {
|
||||
depthStencilDescriptor := objc.ID(class_MTLDepthStencilDescriptor).Send(sel_new)
|
||||
backFaceStencil := depthStencilDescriptor.Send(sel_backFaceStencil)
|
||||
backFaceStencil.Send(sel_setStencilFailureOperation, uintptr(dsd.BackFaceStencil.StencilFailureOperation))
|
||||
@ -742,14 +761,14 @@ func (d Device) MakeDepthStencilState(dsd DepthStencilDescriptor) DepthStencilSt
|
||||
// CompileOptions specifies optional compilation settings for
|
||||
// the graphics or compute functions within a library.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcompileoptions.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcompileoptions?language=objc.
|
||||
type CompileOptions struct {
|
||||
// TODO.
|
||||
}
|
||||
|
||||
// Drawable is a displayable resource that can be rendered or written to.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldrawable.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldrawable?language=objc.
|
||||
type Drawable interface {
|
||||
// Drawable returns the underlying id<MTLDrawable> pointer.
|
||||
Drawable() unsafe.Pointer
|
||||
@ -758,7 +777,7 @@ type Drawable interface {
|
||||
// CommandQueue is a queue that organizes the order
|
||||
// in which command buffers are executed by the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue?language=objc.
|
||||
type CommandQueue struct {
|
||||
commandQueue objc.ID
|
||||
}
|
||||
@ -767,17 +786,17 @@ func (cq CommandQueue) Release() {
|
||||
cq.commandQueue.Send(sel_release)
|
||||
}
|
||||
|
||||
// MakeCommandBuffer creates a command buffer.
|
||||
// CommandBuffer returns a command buffer from the command queue that maintains strong references to resources.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue/1508686-makecommandbuffer.
|
||||
func (cq CommandQueue) MakeCommandBuffer() CommandBuffer {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue/1508686-commandbuffer?language=objc.
|
||||
func (cq CommandQueue) CommandBuffer() CommandBuffer {
|
||||
return CommandBuffer{cq.commandQueue.Send(sel_commandBuffer)}
|
||||
}
|
||||
|
||||
// CommandBuffer is a container that stores encoded commands
|
||||
// that are committed to and executed by the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer?language=objc.
|
||||
type CommandBuffer struct {
|
||||
commandBuffer objc.ID
|
||||
}
|
||||
@ -792,44 +811,43 @@ func (cb CommandBuffer) Release() {
|
||||
|
||||
// Status returns the current stage in the lifetime of the command buffer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443048-status
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443048-status?language=objc.
|
||||
func (cb CommandBuffer) Status() CommandBufferStatus {
|
||||
return CommandBufferStatus(cb.commandBuffer.Send(sel_status))
|
||||
}
|
||||
|
||||
// PresentDrawable registers a drawable presentation to occur as soon as possible.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable?language=objc.
|
||||
func (cb CommandBuffer) PresentDrawable(d Drawable) {
|
||||
cb.commandBuffer.Send(sel_presentDrawable, d.Drawable())
|
||||
}
|
||||
|
||||
// Commit commits this command buffer for execution as soon as possible.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443003-commit.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443003-commit?language=objc.
|
||||
func (cb CommandBuffer) Commit() {
|
||||
cb.commandBuffer.Send(sel_commit)
|
||||
}
|
||||
|
||||
// WaitUntilCompleted waits for the execution of this command buffer to complete.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443039-waituntilcompleted.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443039-waituntilcompleted?language=objc.
|
||||
func (cb CommandBuffer) WaitUntilCompleted() {
|
||||
cb.commandBuffer.Send(sel_waitUntilCompleted)
|
||||
}
|
||||
|
||||
// WaitUntilScheduled blocks execution of the current thread until the command buffer is scheduled.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443036-waituntilscheduled.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443036-waituntilscheduled?language=objc.
|
||||
func (cb CommandBuffer) WaitUntilScheduled() {
|
||||
cb.commandBuffer.Send(sel_waitUntilScheduled)
|
||||
}
|
||||
|
||||
// MakeRenderCommandEncoder creates an encoder object that can
|
||||
// encode graphics rendering commands into this command buffer.
|
||||
// RenderCommandEncoderWithDescriptor creates a render command encoder from a descriptor.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1442999-makerendercommandencoder.
|
||||
func (cb CommandBuffer) MakeRenderCommandEncoder(rpd RenderPassDescriptor) RenderCommandEncoder {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1442999-rendercommandencoderwithdescript?language=objc.
|
||||
func (cb CommandBuffer) RenderCommandEncoderWithDescriptor(rpd RenderPassDescriptor) RenderCommandEncoder {
|
||||
var renderPassDescriptor = objc.ID(class_MTLRenderPassDescriptor).Send(sel_new)
|
||||
var colorAttachments0 = renderPassDescriptor.Send(sel_colorAttachments).Send(sel_objectAtIndexedSubscript, 0)
|
||||
colorAttachments0.Send(sel_setLoadAction, int(rpd.ColorAttachments[0].LoadAction))
|
||||
@ -850,11 +868,11 @@ func (cb CommandBuffer) MakeRenderCommandEncoder(rpd RenderPassDescriptor) Rende
|
||||
return RenderCommandEncoder{CommandEncoder{rce}}
|
||||
}
|
||||
|
||||
// MakeBlitCommandEncoder creates an encoder object that can encode
|
||||
// BlitCommandEncoder creates an encoder object that can encode
|
||||
// memory operation (blit) commands into this command buffer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-makeblitcommandencoder.
|
||||
func (cb CommandBuffer) MakeBlitCommandEncoder() BlitCommandEncoder {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-makeblitcommandencoder?language=objc.
|
||||
func (cb CommandBuffer) BlitCommandEncoder() BlitCommandEncoder {
|
||||
ce := cb.commandBuffer.Send(sel_blitCommandEncoder)
|
||||
return BlitCommandEncoder{CommandEncoder{ce}}
|
||||
}
|
||||
@ -862,14 +880,14 @@ func (cb CommandBuffer) MakeBlitCommandEncoder() BlitCommandEncoder {
|
||||
// CommandEncoder is an encoder that writes sequential GPU commands
|
||||
// into a command buffer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-blitcommandencoder?language=objc.
|
||||
type CommandEncoder struct {
|
||||
commandEncoder objc.ID
|
||||
}
|
||||
|
||||
// EndEncoding declares that all command generation from this encoder is completed.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder/1458038-endencoding.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder/1458038-endencoding?language=objc.
|
||||
func (ce CommandEncoder) EndEncoding() {
|
||||
ce.commandEncoder.Send(sel_endEncoding)
|
||||
}
|
||||
@ -877,7 +895,7 @@ func (ce CommandEncoder) EndEncoding() {
|
||||
// RenderCommandEncoder is an encoder that specifies graphics-rendering commands
|
||||
// and executes graphics functions.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder?language=objc.
|
||||
type RenderCommandEncoder struct {
|
||||
CommandEncoder
|
||||
}
|
||||
@ -888,7 +906,7 @@ func (rce RenderCommandEncoder) Release() {
|
||||
|
||||
// SetRenderPipelineState sets the current render pipeline state object.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515811-setrenderpipelinestate.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515811-setrenderpipelinestate?language=objc.
|
||||
func (rce RenderCommandEncoder) SetRenderPipelineState(rps RenderPipelineState) {
|
||||
rce.commandEncoder.Send(sel_setRenderPipelineState, rps.renderPipelineState)
|
||||
}
|
||||
@ -903,7 +921,7 @@ func (rce RenderCommandEncoder) SetViewport(viewport Viewport) {
|
||||
|
||||
// SetScissorRect sets the scissor rectangle for a fragment scissor test.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515583-setscissorrect
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515583-setscissorrect?language=objc.
|
||||
func (rce RenderCommandEncoder) SetScissorRect(scissorRect ScissorRect) {
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:{MTLScissorRect=qqqq}"))
|
||||
inv.SetTarget(rce.commandEncoder)
|
||||
@ -915,14 +933,14 @@ func (rce RenderCommandEncoder) SetScissorRect(scissorRect ScissorRect) {
|
||||
// SetVertexBuffer sets a buffer for the vertex shader function at an index
|
||||
// in the buffer argument table with an offset that specifies the start of the data.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515829-setvertexbuffer.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515829-setvertexbuffer?language=objc.
|
||||
func (rce RenderCommandEncoder) SetVertexBuffer(buf Buffer, offset, index int) {
|
||||
rce.commandEncoder.Send(sel_setVertexBuffer_offset_atIndex, buf.buffer, offset, index)
|
||||
}
|
||||
|
||||
// SetVertexBytes sets a block of data for the vertex function.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515846-setvertexbytes.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515846-setvertexbytes?language=objc.
|
||||
func (rce RenderCommandEncoder) SetVertexBytes(bytes unsafe.Pointer, length uintptr, index int) {
|
||||
rce.commandEncoder.Send(sel_setVertexBytes_length_atIndex, bytes, length, index)
|
||||
}
|
||||
@ -933,7 +951,7 @@ func (rce RenderCommandEncoder) SetFragmentBytes(bytes unsafe.Pointer, length ui
|
||||
|
||||
// SetFragmentTexture sets a texture for the fragment function at an index in the texture argument table.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515390-setfragmenttexture
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515390-setfragmenttexture?language=objc.
|
||||
func (rce RenderCommandEncoder) SetFragmentTexture(texture Texture, index int) {
|
||||
rce.commandEncoder.Send(sel_setFragmentTexture_atIndex, texture.texture, index)
|
||||
}
|
||||
@ -944,7 +962,7 @@ func (rce RenderCommandEncoder) SetBlendColor(red, green, blue, alpha float32) {
|
||||
|
||||
// SetDepthStencilState sets the depth and stencil test state.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516119-setdepthstencilstate
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516119-setdepthstencilstate?language=objc.
|
||||
func (rce RenderCommandEncoder) SetDepthStencilState(depthStencilState DepthStencilState) {
|
||||
rce.commandEncoder.Send(sel_setDepthStencilState, depthStencilState.depthStencilState)
|
||||
}
|
||||
@ -952,7 +970,7 @@ func (rce RenderCommandEncoder) SetDepthStencilState(depthStencilState DepthSten
|
||||
// DrawPrimitives renders one instance of primitives using vertex data
|
||||
// in contiguous array elements.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516326-drawprimitives.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516326-drawprimitives?language=objc.
|
||||
func (rce RenderCommandEncoder) DrawPrimitives(typ PrimitiveType, vertexStart, vertexCount int) {
|
||||
rce.commandEncoder.Send(sel_drawPrimitives_vertexStart_vertexCount, uintptr(typ), vertexStart, vertexCount)
|
||||
}
|
||||
@ -969,7 +987,7 @@ func (rce RenderCommandEncoder) DrawIndexedPrimitives(typ PrimitiveType, indexCo
|
||||
// BlitCommandEncoder is an encoder that specifies resource copy
|
||||
// and resource synchronization commands.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder?language=objc.
|
||||
type BlitCommandEncoder struct {
|
||||
CommandEncoder
|
||||
}
|
||||
@ -977,7 +995,7 @@ type BlitCommandEncoder struct {
|
||||
// Synchronize flushes any copy of the specified resource from its corresponding
|
||||
// Device caches and, if needed, invalidates any CPU caches.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400775-synchronize.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400775-synchronize?language=objc.
|
||||
func (bce BlitCommandEncoder) Synchronize(resource Resource) {
|
||||
if runtime.GOOS == "ios" {
|
||||
return
|
||||
@ -985,6 +1003,9 @@ func (bce BlitCommandEncoder) Synchronize(resource Resource) {
|
||||
bce.commandEncoder.Send(sel_synchronizeResource, resource.resource())
|
||||
}
|
||||
|
||||
// SynchronizeTexture encodes a command that synchronizes a part of the CPU’s copy of a texture so that it matches the GPU’s copy.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400757-synchronizetexture?language=objc.
|
||||
func (bce BlitCommandEncoder) SynchronizeTexture(texture Texture, slice int, level int) {
|
||||
if runtime.GOOS == "ios" {
|
||||
return
|
||||
@ -992,6 +1013,9 @@ func (bce BlitCommandEncoder) SynchronizeTexture(texture Texture, slice int, lev
|
||||
bce.commandEncoder.Send(sel_synchronizeTexture_slice_level, texture.texture, slice, level)
|
||||
}
|
||||
|
||||
// CopyFromTexture encodes a command that copies image data from a texture’s slice into another slice.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400754-copyfromtexture?language=objc.
|
||||
func (bce BlitCommandEncoder) CopyFromTexture(sourceTexture Texture, sourceSlice int, sourceLevel int, sourceOrigin Origin, sourceSize Size, destinationTexture Texture, destinationSlice int, destinationLevel int, destinationOrigin Origin) {
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:@QQ{MTLOrigin=qqq}{MTLSize=qqq}@QQ{MTLOrigin=qqq}"))
|
||||
inv.SetTarget(bce.commandEncoder)
|
||||
@ -1010,15 +1034,15 @@ func (bce BlitCommandEncoder) CopyFromTexture(sourceTexture Texture, sourceSlice
|
||||
|
||||
// Library is a collection of compiled graphics or compute functions.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtllibrary.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtllibrary?language=objc.
|
||||
type Library struct {
|
||||
library objc.ID
|
||||
}
|
||||
|
||||
// MakeFunction returns a pre-compiled, non-specialized function.
|
||||
// NewFunctionWithName returns a pre-compiled, non-specialized function.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtllibrary/1515524-makefunction.
|
||||
func (l Library) MakeFunction(name string) (Function, error) {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtllibrary/1515524-newfunctionwithname?language=objc.
|
||||
func (l Library) NewFunctionWithName(name string) (Function, error) {
|
||||
f := l.library.Send(sel_newFunctionWithName,
|
||||
cocoa.NSString_alloc().InitWithUTF8String(name).ID,
|
||||
)
|
||||
@ -1028,10 +1052,14 @@ func (l Library) MakeFunction(name string) (Function, error) {
|
||||
return Function{f}, nil
|
||||
}
|
||||
|
||||
func (l Library) Release() {
|
||||
l.library.Send(sel_release)
|
||||
}
|
||||
|
||||
// Texture is a memory allocation for storing formatted
|
||||
// image data that is accessible to the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture?language=objc.
|
||||
type Texture struct {
|
||||
texture objc.ID
|
||||
}
|
||||
@ -1042,7 +1070,9 @@ func NewTexture(texture objc.ID) Texture {
|
||||
}
|
||||
|
||||
// resource implements the Resource interface.
|
||||
func (t Texture) resource() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Pointer(&t.texture)) }
|
||||
func (t Texture) resource() unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&t.texture))
|
||||
}
|
||||
|
||||
func (t Texture) Release() {
|
||||
t.texture.Send(sel_release)
|
||||
@ -1051,7 +1081,7 @@ func (t Texture) Release() {
|
||||
// GetBytes copies a block of pixels from the storage allocation of texture
|
||||
// slice zero into system memory at a specified address.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515751-getbytes.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515751-getbytes?language=objc.
|
||||
func (t Texture) GetBytes(pixelBytes *byte, bytesPerRow uintptr, region Region, level int) {
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:^vQ{MTLRegion={MTLOrigin=qqq}{MTLSize=qqq}}Q"))
|
||||
inv.SetTarget(t.texture)
|
||||
@ -1065,7 +1095,7 @@ func (t Texture) GetBytes(pixelBytes *byte, bytesPerRow uintptr, region Region,
|
||||
|
||||
// ReplaceRegion copies a block of pixels from the caller's pointer into the storage allocation for slice 0 of a texture.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515464-replaceregion
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515464-replaceregion?language=objc.
|
||||
func (t Texture) ReplaceRegion(region Region, level int, pixelBytes unsafe.Pointer, bytesPerRow int) {
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:{MTLRegion={MTLOrigin=qqq}{MTLSize=qqq}}Q^vQ"))
|
||||
inv.SetTarget(t.texture)
|
||||
@ -1079,14 +1109,14 @@ func (t Texture) ReplaceRegion(region Region, level int, pixelBytes unsafe.Point
|
||||
|
||||
// Width is the width of the texture image for the base level mipmap, in pixels.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515339-width
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515339-width?language=objc.
|
||||
func (t Texture) Width() int {
|
||||
return int(t.texture.Send(sel_width))
|
||||
}
|
||||
|
||||
// Height is the height of the texture image for the base level mipmap, in pixels.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515938-height
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515938-height?language=objc.
|
||||
func (t Texture) Height() int {
|
||||
return int(t.texture.Send(sel_height))
|
||||
}
|
||||
@ -1094,13 +1124,19 @@ func (t Texture) Height() int {
|
||||
// Buffer is a memory allocation for storing unformatted data
|
||||
// that is accessible to the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlbuffer.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlbuffer?language=objc.
|
||||
type Buffer struct {
|
||||
buffer objc.ID
|
||||
}
|
||||
|
||||
func (b Buffer) resource() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer)) }
|
||||
// resource implements the Resource interface.
|
||||
func (b Buffer) resource() unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer))
|
||||
}
|
||||
|
||||
// Length returns the logical size of the buffer, in bytes.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlbuffer/1515373-length?language=objc.
|
||||
func (b Buffer) Length() uintptr {
|
||||
return uintptr(b.buffer.Send(sel_length))
|
||||
}
|
||||
@ -1121,13 +1157,9 @@ func (b Buffer) Release() {
|
||||
b.buffer.Send(sel_release)
|
||||
}
|
||||
|
||||
func (b Buffer) Native() unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer))
|
||||
}
|
||||
|
||||
// Function represents a programmable graphics or compute function executed by the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlfunction.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlfunction?language=objc.
|
||||
type Function struct {
|
||||
function objc.ID
|
||||
}
|
||||
@ -1139,7 +1171,7 @@ func (f Function) Release() {
|
||||
// RenderPipelineState contains the graphics functions
|
||||
// and configuration state used in a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinestate.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinestate?language=objc.
|
||||
type RenderPipelineState struct {
|
||||
renderPipelineState objc.ID
|
||||
}
|
||||
@ -1151,7 +1183,7 @@ func (r RenderPipelineState) Release() {
|
||||
// Region is a rectangular block of pixels in an image or texture,
|
||||
// defined by its upper-left corner and its size.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlregion.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlregion?language=objc.
|
||||
type Region struct {
|
||||
Origin Origin // The location of the upper-left corner of the block.
|
||||
Size Size // The size of the block.
|
||||
@ -1160,25 +1192,36 @@ type Region struct {
|
||||
// Origin represents the location of a pixel in an image or texture relative
|
||||
// to the upper-left corner, whose coordinates are (0, 0).
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlorigin.
|
||||
type Origin struct{ X, Y, Z int }
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlorigin?language=objc.
|
||||
type Origin struct {
|
||||
X int
|
||||
Y int
|
||||
Z int
|
||||
}
|
||||
|
||||
// Size represents the set of dimensions that declare the size of an object,
|
||||
// such as an image, texture, threadgroup, or grid.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlsize.
|
||||
type Size struct{ Width, Height, Depth int }
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlsize?language=objc.
|
||||
type Size struct {
|
||||
Width int
|
||||
Height int
|
||||
Depth int
|
||||
}
|
||||
|
||||
// RegionMake2D returns a 2D, rectangular region for image or texture data.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/1515675-mtlregionmake2d.
|
||||
// Reference: https://developer.apple.com/documentation/metal/1515675-mtlregionmake2d?language=objc.
|
||||
func RegionMake2D(x, y, width, height int) Region {
|
||||
return Region{
|
||||
Origin: Origin{x, y, 0},
|
||||
Size: Size{width, height, 1},
|
||||
Origin: Origin{X: x, Y: y, Z: 0},
|
||||
Size: Size{Width: width, Height: height, Depth: 1},
|
||||
}
|
||||
}
|
||||
|
||||
// Viewport is a 3D rectangular region for the viewport clipping.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlviewport?language=objc.
|
||||
type Viewport struct {
|
||||
OriginX float64
|
||||
OriginY float64
|
||||
@ -1188,9 +1231,9 @@ type Viewport struct {
|
||||
ZFar float64
|
||||
}
|
||||
|
||||
// ScissorRect represents a rectangle for the scissor fragment test.
|
||||
// ScissorRect is a rectangle for the scissor fragment test.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlscissorrect
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlscissorrect?language=objc.
|
||||
type ScissorRect struct {
|
||||
X int
|
||||
Y int
|
||||
@ -1200,7 +1243,7 @@ type ScissorRect struct {
|
||||
|
||||
// DepthStencilState is a depth and stencil state object that specifies the depth and stencil configuration and operations used in a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldepthstencilstate
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldepthstencilstate?language=objc.
|
||||
type DepthStencilState struct {
|
||||
depthStencilState objc.ID
|
||||
}
|
||||
@ -1211,7 +1254,7 @@ func (d DepthStencilState) Release() {
|
||||
|
||||
// DepthStencilDescriptor is an object that configures new MTLDepthStencilState objects.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldepthstencildescriptor
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldepthstencildescriptor?language=objc.
|
||||
type DepthStencilDescriptor struct {
|
||||
// BackFaceStencil is the stencil descriptor for back-facing primitives.
|
||||
BackFaceStencil StencilDescriptor
|
||||
@ -1222,7 +1265,7 @@ type DepthStencilDescriptor struct {
|
||||
|
||||
// StencilDescriptor is an object that defines the front-facing or back-facing stencil operations of a depth and stencil state object.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstencildescriptor
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstencildescriptor?language=objc.
|
||||
type StencilDescriptor struct {
|
||||
// StencilFailureOperation is the operation that is performed to update the values in the stencil attachment when the stencil test fails.
|
||||
StencilFailureOperation StencilOperation
|
||||
|
@ -56,15 +56,15 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
return in.color;
|
||||
}
|
||||
`
|
||||
lib, err := device.MakeLibrary(source, mtl.CompileOptions{})
|
||||
lib, err := device.NewLibraryWithSource(source, mtl.CompileOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
vs, err := lib.MakeFunction("VertexShader")
|
||||
vs, err := lib.NewFunctionWithName("VertexShader")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fs, err := lib.MakeFunction("FragmentShader")
|
||||
fs, err := lib.NewFunctionWithName("FragmentShader")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -72,7 +72,7 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
rpld.VertexFunction = vs
|
||||
rpld.FragmentFunction = fs
|
||||
rpld.ColorAttachments[0].PixelFormat = mtl.PixelFormatRGBA8UNorm
|
||||
rps, err := device.MakeRenderPipelineState(rpld)
|
||||
rps, err := device.NewRenderPipelineStateWithDescriptor(rpld)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -87,7 +87,7 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
{f32.Vec4{-0.75, -0.75, 0, 1}, f32.Vec4{0, 1, 0, 1}},
|
||||
{f32.Vec4{+0.75, -0.75, 0, 1}, f32.Vec4{0, 0, 1, 1}},
|
||||
}
|
||||
vertexBuffer := device.MakeBufferWithBytes(unsafe.Pointer(&vertexData[0]), unsafe.Sizeof(vertexData), mtl.ResourceStorageModeManaged)
|
||||
vertexBuffer := device.NewBufferWithBytes(unsafe.Pointer(&vertexData[0]), unsafe.Sizeof(vertexData), mtl.ResourceStorageModeManaged)
|
||||
|
||||
// Create an output texture to render into.
|
||||
td := mtl.TextureDescriptor{
|
||||
@ -97,10 +97,10 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
Height: 512,
|
||||
StorageMode: mtl.StorageModeManaged,
|
||||
}
|
||||
texture := device.MakeTexture(td)
|
||||
texture := device.NewTextureWithDescriptor(td)
|
||||
|
||||
cq := device.MakeCommandQueue()
|
||||
cb := cq.MakeCommandBuffer()
|
||||
cq := device.NewCommandQueue()
|
||||
cb := cq.CommandBuffer()
|
||||
|
||||
// Encode all render commands.
|
||||
var rpd mtl.RenderPassDescriptor
|
||||
@ -108,14 +108,14 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) {
|
||||
rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore
|
||||
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{Red: 0.35, Green: 0.65, Blue: 0.85, Alpha: 1}
|
||||
rpd.ColorAttachments[0].Texture = texture
|
||||
rce := cb.MakeRenderCommandEncoder(rpd)
|
||||
rce := cb.RenderCommandEncoderWithDescriptor(rpd)
|
||||
rce.SetRenderPipelineState(rps)
|
||||
rce.SetVertexBuffer(vertexBuffer, 0, 0)
|
||||
rce.DrawPrimitives(mtl.PrimitiveTypeTriangle, 0, 3)
|
||||
rce.EndEncoding()
|
||||
|
||||
// Encode all blit commands.
|
||||
bce := cb.MakeBlitCommandEncoder()
|
||||
bce := cb.BlitCommandEncoder()
|
||||
bce.Synchronize(texture)
|
||||
bce.EndEncoding()
|
||||
|
||||
|
@ -16,6 +16,7 @@ package metal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
|
||||
@ -23,6 +24,37 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl"
|
||||
)
|
||||
|
||||
type precompiledLibraries struct {
|
||||
binaries map[shaderir.SourceHash][]byte
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func (c *precompiledLibraries) put(hash shaderir.SourceHash, bin []byte) {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
if c.binaries == nil {
|
||||
c.binaries = map[shaderir.SourceHash][]byte{}
|
||||
}
|
||||
if _, ok := c.binaries[hash]; ok {
|
||||
panic(fmt.Sprintf("metal: the precompiled library for the hash %s is already registered", hash.String()))
|
||||
}
|
||||
c.binaries[hash] = bin
|
||||
}
|
||||
|
||||
func (c *precompiledLibraries) get(hash shaderir.SourceHash) []byte {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
return c.binaries[hash]
|
||||
}
|
||||
|
||||
var thePrecompiledLibraries precompiledLibraries
|
||||
|
||||
func RegisterPrecompiledLibrary(source []byte, bin []byte) {
|
||||
thePrecompiledLibraries.put(shaderir.CalcSourceHash(source), bin)
|
||||
}
|
||||
|
||||
type shaderRpsKey struct {
|
||||
blend graphicsdriver.Blend
|
||||
stencilMode stencilMode
|
||||
@ -33,9 +65,12 @@ type Shader struct {
|
||||
id graphicsdriver.ShaderID
|
||||
|
||||
ir *shaderir.Program
|
||||
lib mtl.Library
|
||||
fs mtl.Function
|
||||
vs mtl.Function
|
||||
rpss map[shaderRpsKey]mtl.RenderPipelineState
|
||||
|
||||
libraryPrecompiled bool
|
||||
}
|
||||
|
||||
func newShader(device mtl.Device, id graphicsdriver.ShaderID, program *shaderir.Program) (*Shader, error) {
|
||||
@ -60,21 +95,42 @@ func (s *Shader) Dispose() {
|
||||
}
|
||||
s.vs.Release()
|
||||
s.fs.Release()
|
||||
// Do not release s.lib if this is precompiled. This is a shared precompiled library.
|
||||
if !s.libraryPrecompiled {
|
||||
s.lib.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shader) init(device mtl.Device) error {
|
||||
src := msl.Compile(s.ir)
|
||||
lib, err := device.MakeLibrary(src, mtl.CompileOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src)
|
||||
var src string
|
||||
if libBin := thePrecompiledLibraries.get(s.ir.SourceHash); len(libBin) > 0 {
|
||||
lib, err := device.NewLibraryWithData(libBin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.lib = lib
|
||||
} else {
|
||||
src = msl.Compile(s.ir)
|
||||
lib, err := device.NewLibraryWithSource(src, mtl.CompileOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src)
|
||||
}
|
||||
s.lib = lib
|
||||
}
|
||||
vs, err := lib.MakeFunction(msl.VertexName)
|
||||
|
||||
vs, err := s.lib.NewFunctionWithName(msl.VertexName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src)
|
||||
if src != "" {
|
||||
return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src)
|
||||
}
|
||||
return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w", err)
|
||||
}
|
||||
fs, err := lib.MakeFunction(msl.FragmentName)
|
||||
fs, err := s.lib.NewFunctionWithName(msl.FragmentName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src)
|
||||
if src != "" {
|
||||
return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src)
|
||||
}
|
||||
return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w", err)
|
||||
}
|
||||
s.fs = fs
|
||||
s.vs = vs
|
||||
@ -120,7 +176,7 @@ func (s *Shader) RenderPipelineState(view *view, blend graphicsdriver.Blend, ste
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone
|
||||
}
|
||||
|
||||
rps, err := view.getMTLDevice().MakeRenderPipelineState(rpld)
|
||||
rps, err := view.getMTLDevice().NewRenderPipelineStateWithDescriptor(rpld)
|
||||
if err != nil {
|
||||
return mtl.RenderPipelineState{}, err
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ func (v *view) colorPixelFormat() mtl.PixelFormat {
|
||||
func (v *view) initialize(device mtl.Device) error {
|
||||
v.device = device
|
||||
|
||||
ml, err := ca.MakeMetalLayer()
|
||||
ml, err := ca.NewMetalLayer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ type context struct {
|
||||
|
||||
locationCache *locationCache
|
||||
screenFramebuffer framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
|
||||
mrtFramebuffer framebufferNative // The dynamic framebuffer used for MRT operations
|
||||
lastFramebuffer framebufferNative
|
||||
lastTexture textureNative
|
||||
lastRenderbuffer renderbufferNative
|
||||
@ -110,8 +111,6 @@ type context struct {
|
||||
lastBlend graphicsdriver.Blend
|
||||
maxTextureSize int
|
||||
maxTextureSizeOnce sync.Once
|
||||
highp bool
|
||||
highpOnce sync.Once
|
||||
initOnce sync.Once
|
||||
}
|
||||
|
||||
@ -139,26 +138,25 @@ func (c *context) bindFramebuffer(f framebufferNative) {
|
||||
c.lastFramebuffer = f
|
||||
}
|
||||
|
||||
func (c *context) setViewport(f *framebuffer) {
|
||||
c.bindFramebuffer(f.native)
|
||||
if c.lastViewportWidth == f.viewportWidth && c.lastViewportHeight == f.viewportHeight {
|
||||
func (c *context) setViewport(width, height int, screen bool) {
|
||||
if c.lastViewportWidth == width && c.lastViewportHeight == height {
|
||||
return
|
||||
}
|
||||
|
||||
// On some environments, viewport size must be within the framebuffer size.
|
||||
// e.g. Edge (#71), Chrome on GPD Pocket (#420), macOS Mojave (#691).
|
||||
// Use the same size of the framebuffer here.
|
||||
c.ctx.Viewport(0, 0, int32(f.viewportWidth), int32(f.viewportHeight))
|
||||
c.ctx.Viewport(0, 0, int32(width), int32(height))
|
||||
|
||||
// glViewport must be called at least at every frame on iOS.
|
||||
// As the screen framebuffer is the last render target, next SetViewport should be
|
||||
// the first call at a frame.
|
||||
if f.native == c.screenFramebuffer {
|
||||
if screen {
|
||||
c.lastViewportWidth = 0
|
||||
c.lastViewportHeight = 0
|
||||
} else {
|
||||
c.lastViewportWidth = f.viewportWidth
|
||||
c.lastViewportHeight = f.viewportHeight
|
||||
c.lastViewportWidth = width
|
||||
c.lastViewportHeight = height
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,16 +262,6 @@ func (c *context) framebufferPixels(buf []byte, f *framebuffer, region image.Rec
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) framebufferPixelsToBuffer(f *framebuffer, buffer buffer, width, height int) {
|
||||
c.ctx.Flush()
|
||||
|
||||
c.bindFramebuffer(f.native)
|
||||
|
||||
c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, uint32(buffer))
|
||||
c.ctx.ReadPixels(nil, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE)
|
||||
c.ctx.BindBuffer(gl.PIXEL_PACK_BUFFER, 0)
|
||||
}
|
||||
|
||||
func (c *context) deleteTexture(t textureNative) {
|
||||
if c.lastTexture == t {
|
||||
c.lastTexture = 0
|
||||
@ -357,7 +345,7 @@ func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) e
|
||||
|
||||
c.ctx.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, uint32(r))
|
||||
if s := c.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
|
||||
return errors.New(fmt.Sprintf("opengl: glFramebufferRenderbuffer failed: %d", s))
|
||||
return fmt.Errorf("opengl: glFramebufferRenderbuffer failed: %d", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ const (
|
||||
BLEND = 0x0BE2
|
||||
CLAMP_TO_EDGE = 0x812F
|
||||
COLOR_ATTACHMENT0 = 0x8CE0
|
||||
COLOR_BUFFER_BIT = 0x4000
|
||||
COMPILE_STATUS = 0x8B81
|
||||
DECR_WRAP = 0x8508
|
||||
DEPTH24_STENCIL8 = 0x88F0
|
||||
@ -52,6 +53,7 @@ const (
|
||||
MIN = 0x8007
|
||||
NEAREST = 0x2600
|
||||
NO_ERROR = 0
|
||||
NONE = 0
|
||||
NOTEQUAL = 0x0205
|
||||
ONE = 1
|
||||
ONE_MINUS_DST_ALPHA = 0x0305
|
||||
|
@ -293,6 +293,14 @@ func (d *DebugContext) DisableVertexAttribArray(arg0 uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DebugContext) DrawBuffers(arg0 []uint32) {
|
||||
d.Context.DrawBuffers(arg0)
|
||||
fmt.Fprintln(os.Stderr, "DrawBuffers")
|
||||
if e := d.Context.GetError(); e != NO_ERROR {
|
||||
panic(fmt.Sprintf("gl: GetError() returned %d at DrawBuffers", e))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DebugContext) DrawElements(arg0 uint32, arg1 int32, arg2 uint32, arg3 int) {
|
||||
d.Context.DrawElements(arg0, arg1, arg2, arg3)
|
||||
fmt.Fprintln(os.Stderr, "DrawElements")
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-FileCopyrightText: 2014 Eric Woroshow
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !darwin && !js && !windows && !playstation5
|
||||
//go:build nintendosdk
|
||||
|
||||
package gl
|
||||
|
||||
@ -128,6 +128,10 @@ package gl
|
||||
// typedef void (*fn)(GLuint index);
|
||||
// ((fn)(fnptr))(index);
|
||||
// }
|
||||
// static void glowDrawBuffers(uintptr_t fnptr, GLsizei n, const GLenum* bufs) {
|
||||
// typedef void (*fn)(GLsizei n, const GLenum* bufs);
|
||||
// ((fn)(fnptr))(n, bufs);
|
||||
// }
|
||||
// static void glowDrawElements(uintptr_t fnptr, GLenum mode, GLsizei count, GLenum type, const uintptr_t indices) {
|
||||
// typedef void (*fn)(GLenum mode, GLsizei count, GLenum type, const uintptr_t indices);
|
||||
// ((fn)(fnptr))(mode, count, type, indices);
|
||||
@ -351,6 +355,7 @@ type defaultContext struct {
|
||||
gpDeleteVertexArrays C.uintptr_t
|
||||
gpDisable C.uintptr_t
|
||||
gpDisableVertexAttribArray C.uintptr_t
|
||||
gpDrawBuffers C.uintptr_t
|
||||
gpDrawElements C.uintptr_t
|
||||
gpEnable C.uintptr_t
|
||||
gpEnableVertexAttribArray C.uintptr_t
|
||||
@ -565,6 +570,10 @@ func (c *defaultContext) DisableVertexAttribArray(index uint32) {
|
||||
C.glowDisableVertexAttribArray(c.gpDisableVertexAttribArray, C.GLuint(index))
|
||||
}
|
||||
|
||||
func (c *defaultContext) DrawBuffers(bufs []uint32) {
|
||||
C.glowDrawBuffers(c.gpDrawBuffers, C.GLsizei(len(bufs)), (*C.GLenum)(unsafe.Pointer(&bufs[0])))
|
||||
}
|
||||
|
||||
func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) {
|
||||
C.glowDrawElements(c.gpDrawElements, C.GLenum(mode), C.GLsizei(count), C.GLenum(xtype), C.uintptr_t(offset))
|
||||
}
|
||||
@ -801,6 +810,7 @@ func (c *defaultContext) LoadFunctions() error {
|
||||
c.gpDeleteVertexArrays = C.uintptr_t(g.get("glDeleteVertexArrays"))
|
||||
c.gpDisable = C.uintptr_t(g.get("glDisable"))
|
||||
c.gpDisableVertexAttribArray = C.uintptr_t(g.get("glDisableVertexAttribArray"))
|
||||
c.gpDrawBuffers = C.uintptr_t(g.get("glDrawBuffers"))
|
||||
c.gpDrawElements = C.uintptr_t(g.get("glDrawElements"))
|
||||
c.gpEnable = C.uintptr_t(g.get("glEnable"))
|
||||
c.gpEnableVertexAttribArray = C.uintptr_t(g.get("glEnableVertexAttribArray"))
|
||||
|
@ -54,6 +54,7 @@ type defaultContext struct {
|
||||
fnDeleteVertexArray js.Value
|
||||
fnDisable js.Value
|
||||
fnDisableVertexAttribArray js.Value
|
||||
fnDrawBuffers js.Value
|
||||
fnDrawElements js.Value
|
||||
fnEnable js.Value
|
||||
fnEnableVertexAttribArray js.Value
|
||||
@ -184,6 +185,7 @@ func NewDefaultContext(v js.Value) (Context, error) {
|
||||
fnDeleteVertexArray: v.Get("deleteVertexArray").Call("bind", v),
|
||||
fnDisable: v.Get("disable").Call("bind", v),
|
||||
fnDisableVertexAttribArray: v.Get("disableVertexAttribArray").Call("bind", v),
|
||||
fnDrawBuffers: v.Get("drawBuffers").Call("bind", v),
|
||||
fnDrawElements: v.Get("drawElements").Call("bind", v),
|
||||
fnEnable: v.Get("enable").Call("bind", v),
|
||||
fnEnableVertexAttribArray: v.Get("enableVertexAttribArray").Call("bind", v),
|
||||
@ -384,6 +386,11 @@ func (c *defaultContext) DisableVertexAttribArray(index uint32) {
|
||||
c.fnDisableVertexAttribArray.Invoke(index)
|
||||
}
|
||||
|
||||
func (c *defaultContext) DrawBuffers(bufs []uint32) {
|
||||
arr := jsutil.NewUint32Array(bufs)
|
||||
c.fnDrawBuffers.Invoke(arr)
|
||||
}
|
||||
|
||||
func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, offset int) {
|
||||
c.fnDrawElements.Invoke(mode, count, xtype, offset)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build darwin || windows
|
||||
//go:build (darwin || freebsd || linux || netbsd || openbsd || windows) && !nintendosdk && !playstation5
|
||||
|
||||
package gl
|
||||
|
||||
@ -51,6 +51,7 @@ type defaultContext struct {
|
||||
gpDeleteVertexArrays uintptr
|
||||
gpDisable uintptr
|
||||
gpDisableVertexAttribArray uintptr
|
||||
gpDrawBuffers uintptr
|
||||
gpDrawElements uintptr
|
||||
gpEnable uintptr
|
||||
gpEnableVertexAttribArray uintptr
|
||||
@ -269,6 +270,10 @@ func (c *defaultContext) DrawElements(mode uint32, count int32, xtype uint32, of
|
||||
purego.SyscallN(c.gpDrawElements, uintptr(mode), uintptr(count), uintptr(xtype), uintptr(offset))
|
||||
}
|
||||
|
||||
func (c *defaultContext) DrawBuffers(buffers []uint32) {
|
||||
purego.SyscallN(c.gpDrawBuffers, uintptr(len(buffers)), uintptr(unsafe.Pointer(&buffers[0])))
|
||||
}
|
||||
|
||||
func (c *defaultContext) Enable(cap uint32) {
|
||||
purego.SyscallN(c.gpEnable, uintptr(cap))
|
||||
}
|
||||
@ -501,6 +506,7 @@ func (c *defaultContext) LoadFunctions() error {
|
||||
c.gpDeleteVertexArrays = g.get("glDeleteVertexArrays")
|
||||
c.gpDisable = g.get("glDisable")
|
||||
c.gpDisableVertexAttribArray = g.get("glDisableVertexAttribArray")
|
||||
c.gpDrawBuffers = g.get("glDrawBuffers")
|
||||
c.gpDrawElements = g.get("glDrawElements")
|
||||
c.gpEnable = g.get("glEnable")
|
||||
c.gpEnableVertexAttribArray = g.get("glEnableVertexAttribArray")
|
||||
|
@ -60,6 +60,7 @@ type Context interface {
|
||||
Disable(cap uint32)
|
||||
DisableVertexAttribArray(index uint32)
|
||||
DrawElements(mode uint32, count int32, xtype uint32, offset int)
|
||||
DrawBuffers(buffers []uint32)
|
||||
Enable(cap uint32)
|
||||
EnableVertexAttribArray(index uint32)
|
||||
Flush()
|
||||
|
@ -16,38 +16,18 @@
|
||||
|
||||
package gl
|
||||
|
||||
// #cgo LDFLAGS: -ldl
|
||||
//
|
||||
// #include <dlfcn.h>
|
||||
// #include <stdlib.h>
|
||||
//
|
||||
// static void* getProcAddressGL(void* libGL, const char* name) {
|
||||
// static void*(*glXGetProcAddress)(const char*);
|
||||
// if (!glXGetProcAddress) {
|
||||
// glXGetProcAddress = dlsym(libGL, "glXGetProcAddress");
|
||||
// if (!glXGetProcAddress) {
|
||||
// glXGetProcAddress = dlsym(libGL, "glXGetProcAddressARB");
|
||||
// }
|
||||
// }
|
||||
// return glXGetProcAddress(name);
|
||||
// }
|
||||
//
|
||||
// static void* getProcAddressGLES(void* libGLES, const char* name) {
|
||||
// return dlsym(libGLES, name);
|
||||
// }
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
var (
|
||||
libGL unsafe.Pointer
|
||||
libGLES unsafe.Pointer
|
||||
libGL uintptr
|
||||
libGLES uintptr
|
||||
)
|
||||
|
||||
func (c *defaultContext) init() error {
|
||||
@ -72,10 +52,8 @@ func (c *defaultContext) init() error {
|
||||
// [1] https://github.com/glfw/glfw/commit/55aad3c37b67f17279378db52da0a3ab81bbf26d
|
||||
// [2] https://github.com/glfw/glfw/commit/c18851f52ec9704eb06464058a600845ec1eada1
|
||||
for _, name := range []string{"libGL.so", "libGL.so.2", "libGL.so.1", "libGL.so.0"} {
|
||||
cname := C.CString(name)
|
||||
lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL)
|
||||
C.free(unsafe.Pointer(cname))
|
||||
if lib != nil {
|
||||
lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err == nil {
|
||||
libGL = lib
|
||||
return nil
|
||||
}
|
||||
@ -84,10 +62,8 @@ func (c *defaultContext) init() error {
|
||||
|
||||
// Try OpenGL ES.
|
||||
for _, name := range []string{"libGLESv2.so", "libGLESv2.so.2", "libGLESv2.so.1", "libGLESv2.so.0"} {
|
||||
cname := C.CString(name)
|
||||
lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL)
|
||||
C.free(unsafe.Pointer(cname))
|
||||
if lib != nil {
|
||||
lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err == nil {
|
||||
libGLES = lib
|
||||
c.isES = true
|
||||
return nil
|
||||
@ -99,19 +75,32 @@ func (c *defaultContext) init() error {
|
||||
|
||||
func (c *defaultContext) getProcAddress(name string) (uintptr, error) {
|
||||
if c.isES {
|
||||
return getProcAddressGLES(name), nil
|
||||
return getProcAddressGLES(name)
|
||||
}
|
||||
return getProcAddressGL(name), nil
|
||||
return getProcAddressGL(name)
|
||||
}
|
||||
|
||||
func getProcAddressGL(name string) uintptr {
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
return uintptr(C.getProcAddressGL(libGL, cname))
|
||||
var glXGetProcAddress func(name string) uintptr
|
||||
|
||||
func getProcAddressGL(name string) (uintptr, error) {
|
||||
if glXGetProcAddress == nil {
|
||||
if _, err := purego.Dlsym(libGL, "glXGetProcAddress"); err == nil {
|
||||
purego.RegisterLibFunc(&glXGetProcAddress, libGL, "glXGetProcAddress")
|
||||
} else if _, err := purego.Dlsym(libGL, "glXGetProcAddressARB"); err == nil {
|
||||
purego.RegisterLibFunc(&glXGetProcAddress, libGL, "glXGetProcAddressARB")
|
||||
}
|
||||
}
|
||||
if glXGetProcAddress == nil {
|
||||
return 0, fmt.Errorf("gl: failed to find glXGetProcAddress or glXGetProcAddressARB in libGL.so")
|
||||
}
|
||||
|
||||
return glXGetProcAddress(name), nil
|
||||
}
|
||||
|
||||
func getProcAddressGLES(name string) uintptr {
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
return uintptr(C.getProcAddressGLES(libGLES, cname))
|
||||
func getProcAddressGLES(name string) (uintptr, error) {
|
||||
proc, err := purego.Dlsym(libGLES, name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return proc, nil
|
||||
}
|
||||
|
@ -198,18 +198,88 @@ func (g *Graphics) uniformVariableName(idx int) string {
|
||||
return name
|
||||
}
|
||||
|
||||
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
func (g *Graphics) DrawTriangles(dstIDs [graphics.ShaderDstImageCount]graphicsdriver.ImageID, srcIDs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
if shaderID == graphicsdriver.InvalidShaderID {
|
||||
return fmt.Errorf("opengl: shader ID is invalid")
|
||||
}
|
||||
|
||||
destination := g.images[dstID]
|
||||
|
||||
g.drawCalled = true
|
||||
|
||||
if err := destination.setViewport(); err != nil {
|
||||
return err
|
||||
targetCount := 0
|
||||
firstTarget := -1
|
||||
var dsts [graphics.ShaderDstImageCount]*Image
|
||||
for i, dstID := range dstIDs {
|
||||
if dstID == graphicsdriver.InvalidImageID {
|
||||
continue
|
||||
}
|
||||
dst := g.images[dstIDs[i]]
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
if firstTarget == -1 {
|
||||
firstTarget = i
|
||||
}
|
||||
if err := dst.ensureFramebuffer(); err != nil {
|
||||
return err
|
||||
}
|
||||
dsts[i] = dst
|
||||
targetCount++
|
||||
}
|
||||
|
||||
f := uint32(dsts[firstTarget].framebuffer.native)
|
||||
// If the number of targets is more than one, or if the only target is the first one, then
|
||||
// it is safe to assume that MRT is used.
|
||||
// Also, it only matters in order to specify empty targets/viewports when not all slots are
|
||||
// being filled.
|
||||
usesMRT := firstTarget > 0 || targetCount > 1
|
||||
if usesMRT {
|
||||
f = uint32(g.context.mrtFramebuffer)
|
||||
// Create the initial MRT framebuffer
|
||||
if f == 0 {
|
||||
f = g.context.ctx.CreateFramebuffer()
|
||||
if f <= 0 {
|
||||
return fmt.Errorf("opengl: creating framebuffer failed: the returned value is not positive but %d", f)
|
||||
}
|
||||
g.context.mrtFramebuffer = framebufferNative(f)
|
||||
}
|
||||
|
||||
g.context.bindFramebuffer(framebufferNative(f))
|
||||
|
||||
// Reset color attachments
|
||||
if s := g.context.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s == gl.FRAMEBUFFER_COMPLETE {
|
||||
g.context.ctx.Clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)
|
||||
}
|
||||
for i, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
g.context.ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0+uint32(i), gl.TEXTURE_2D, uint32(dst.texture), 0)
|
||||
}
|
||||
if s := g.context.ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
|
||||
if s != 0 {
|
||||
return fmt.Errorf("opengl: creating framebuffer failed: %v", s)
|
||||
}
|
||||
if e := g.context.ctx.GetError(); e != gl.NO_ERROR {
|
||||
return fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e)
|
||||
}
|
||||
return fmt.Errorf("opengl: creating framebuffer failed: unknown error")
|
||||
}
|
||||
// Color attachments
|
||||
var attached []uint32
|
||||
for i, dst := range dsts {
|
||||
if dst == nil {
|
||||
attached = append(attached, gl.NONE)
|
||||
continue
|
||||
}
|
||||
attached = append(attached, uint32(gl.COLOR_ATTACHMENT0+i))
|
||||
}
|
||||
g.context.ctx.DrawBuffers(attached)
|
||||
} else {
|
||||
g.context.bindFramebuffer(framebufferNative(f))
|
||||
}
|
||||
|
||||
w, h := dsts[firstTarget].viewportSize() //.framebuffer.viewportWidth, dsts[firstTarget].framebuffer.viewportHeight
|
||||
g.context.setViewport(w, h, dsts[firstTarget].screen)
|
||||
|
||||
g.context.blend(blend)
|
||||
|
||||
shader := g.shaders[shaderID]
|
||||
@ -232,7 +302,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
}
|
||||
|
||||
// In OpenGL, the NDC's Y direction is upward, so flip the Y direction for the final framebuffer.
|
||||
if destination.screen {
|
||||
if !usesMRT && dsts[firstTarget].screen {
|
||||
const idx = graphics.ProjectionMatrixUniformVariableIndex
|
||||
// Invert the sign bits as float32 values.
|
||||
g.uniformVars[idx].value[1] ^= 1 << 31
|
||||
@ -241,7 +311,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
g.uniformVars[idx].value[13] ^= 1 << 31
|
||||
}
|
||||
|
||||
var imgs [graphics.ShaderImageCount]textureVariable
|
||||
var imgs [graphics.ShaderSrcImageCount]textureVariable
|
||||
for i, srcID := range srcIDs {
|
||||
if srcID == graphicsdriver.InvalidImageID {
|
||||
continue
|
||||
@ -259,8 +329,8 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
}
|
||||
g.uniformVars = g.uniformVars[:0]
|
||||
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if err := destination.ensureStencilBuffer(); err != nil {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
if err := dsts[firstTarget].ensureStencilBuffer(framebufferNative(f)); err != nil {
|
||||
return err
|
||||
}
|
||||
g.context.ctx.Enable(gl.STENCIL_TEST)
|
||||
@ -273,15 +343,17 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
int32(dstRegion.Region.Dx()),
|
||||
int32(dstRegion.Region.Dy()),
|
||||
)
|
||||
|
||||
switch fillRule {
|
||||
case graphicsdriver.NonZero:
|
||||
case graphicsdriver.FillRuleNonZero:
|
||||
g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT)
|
||||
g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
|
||||
g.context.ctx.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP)
|
||||
g.context.ctx.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP)
|
||||
g.context.ctx.ColorMask(false, false, false, false)
|
||||
|
||||
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
case graphicsdriver.EvenOdd:
|
||||
case graphicsdriver.FillRuleEvenOdd:
|
||||
g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT)
|
||||
g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
|
||||
g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.INVERT)
|
||||
@ -289,7 +361,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
|
||||
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
}
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
g.context.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff)
|
||||
g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.KEEP)
|
||||
g.context.ctx.ColorMask(true, true, true, true)
|
||||
@ -298,10 +370,14 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
indexOffset += dstRegion.IndexCount
|
||||
}
|
||||
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
g.context.ctx.Disable(gl.STENCIL_TEST)
|
||||
}
|
||||
|
||||
// Detach existing color attachments
|
||||
//g.context.bindFramebuffer(fb)
|
||||
//TODO:
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -60,14 +60,6 @@ func (i *Image) Dispose() {
|
||||
i.graphics.removeImage(i)
|
||||
}
|
||||
|
||||
func (i *Image) setViewport() error {
|
||||
if err := i.ensureFramebuffer(); err != nil {
|
||||
return err
|
||||
}
|
||||
i.graphics.context.setViewport(i.framebuffer)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Image) ReadPixels(args []graphicsdriver.PixelsArgs) error {
|
||||
if err := i.ensureFramebuffer(); err != nil {
|
||||
return err
|
||||
@ -109,14 +101,14 @@ func (i *Image) ensureFramebuffer() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Image) ensureStencilBuffer() error {
|
||||
func (i *Image) ensureStencilBuffer(f framebufferNative) error {
|
||||
if i.stencil != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := i.ensureFramebuffer(); err != nil {
|
||||
/*if err := i.ensureFramebuffer(); err != nil {
|
||||
return err
|
||||
}
|
||||
}*/
|
||||
|
||||
r, err := i.graphics.context.newRenderbuffer(i.viewportSize())
|
||||
if err != nil {
|
||||
@ -124,7 +116,7 @@ func (i *Image) ensureStencilBuffer() error {
|
||||
}
|
||||
i.stencil = r
|
||||
|
||||
if err := i.graphics.context.bindStencilBuffer(i.framebuffer.native, i.stencil); err != nil {
|
||||
if err := i.graphics.context.bindStencilBuffer(f, i.stencil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -259,7 +259,7 @@ func (g *Graphics) textureVariableName(idx int) string {
|
||||
}
|
||||
|
||||
// useProgram uses the program (programTexture).
|
||||
func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textures [graphics.ShaderImageCount]textureVariable) error {
|
||||
func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textures [graphics.ShaderSrcImageCount]textureVariable) error {
|
||||
if g.state.lastProgram != program {
|
||||
g.context.ctx.UseProgram(uint32(program))
|
||||
|
||||
|
@ -116,7 +116,7 @@ func (g *Graphics) NewShader(program *shaderir.Program) (graphicsdriver.Shader,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *Graphics) DrawTriangles(dst graphicsdriver.ImageID, srcs [graphics.ShaderImageCount]graphicsdriver.ImageID, shader graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
func (g *Graphics) DrawTriangles(dsts [graphics.ShaderDstImageCount]graphicsdriver.ImageID, srcs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shader graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
23
internal/graphicsdriver/playstation5/shader_paystation5.go
Normal file
23
internal/graphicsdriver/playstation5/shader_paystation5.go
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build playstation5
|
||||
|
||||
// This is a separated pure Go file so that the `shaderprecomp` package can use this without Cgo.
|
||||
|
||||
package playstation5
|
||||
|
||||
func RegisterPrecompiledShaders(source []byte, vertex, pixel []byte) {
|
||||
// TODO: Implement this.
|
||||
}
|
@ -24,6 +24,7 @@ var (
|
||||
uint8Array = js.Global().Get("Uint8Array")
|
||||
float32Array = js.Global().Get("Float32Array")
|
||||
int32Array = js.Global().Get("Int32Array")
|
||||
uint32Array = js.Global().Get("Uint32Array")
|
||||
)
|
||||
|
||||
var (
|
||||
@ -40,8 +41,11 @@ var (
|
||||
// temporaryFloat32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
|
||||
temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
|
||||
|
||||
// temporaryInt32Array is a Float32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
|
||||
// temporaryInt32Array is a Int32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
|
||||
temporaryInt32Array = int32Array.New(temporaryArrayBuffer)
|
||||
|
||||
// temporaryUint32Array is a Uint32ArrayBuffer whose underlying buffer is always temporaryArrayBuffer.
|
||||
temporaryUint32Array = uint32Array.New(temporaryArrayBuffer)
|
||||
)
|
||||
|
||||
func ensureTemporaryArrayBufferSize(byteLength int) {
|
||||
@ -54,6 +58,7 @@ func ensureTemporaryArrayBufferSize(byteLength int) {
|
||||
temporaryUint8Array = uint8Array.New(temporaryArrayBuffer)
|
||||
temporaryFloat32Array = float32Array.New(temporaryArrayBuffer)
|
||||
temporaryInt32Array = int32Array.New(temporaryArrayBuffer)
|
||||
temporaryUint32Array = uint32Array.New(temporaryArrayBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,3 +106,11 @@ func TemporaryInt32Array(minLength int, data []int32) js.Value {
|
||||
copySliceToTemporaryArrayBuffer(data)
|
||||
return temporaryInt32Array
|
||||
}
|
||||
|
||||
// NewUint32Array returns a Uint32Array whose length is equal to the length of data.
|
||||
func NewUint32Array(data []uint32) js.Value {
|
||||
ensureTemporaryArrayBufferSize(len(data) * 4)
|
||||
copySliceToTemporaryArrayBuffer(data)
|
||||
a := temporaryUint32Array.Call("slice", 0, len(data))
|
||||
return a
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ func (m *Mipmap) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byt
|
||||
return m.orig.ReadPixels(graphicsDriver, pixels, region)
|
||||
}
|
||||
|
||||
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool) {
|
||||
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Mipmap, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool) {
|
||||
if len(indices) == 0 {
|
||||
return
|
||||
}
|
||||
@ -103,7 +103,7 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices
|
||||
}
|
||||
}
|
||||
|
||||
var imgs [graphics.ShaderImageCount]*buffered.Image
|
||||
var imgs [graphics.ShaderSrcImageCount]*buffered.Image
|
||||
for i, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
@ -127,6 +127,81 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageCount]*Mipmap, vertices
|
||||
m.deallocateMipmaps()
|
||||
}
|
||||
|
||||
func DrawTrianglesMRT(dsts [graphics.ShaderDstImageCount]*Mipmap, srcs [graphics.ShaderSrcImageCount]*Mipmap, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool) {
|
||||
if len(indices) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
level := 0
|
||||
// TODO: Do we need to check all the sources' states of being volatile?
|
||||
if !canSkipMipmap && srcs[0] != nil && canUseMipmap(srcs[0].imageType) {
|
||||
level = math.MaxInt32
|
||||
for i := 0; i < len(indices)/3; i++ {
|
||||
const n = graphics.VertexFloatCount
|
||||
dx0 := vertices[n*indices[3*i]+0]
|
||||
dy0 := vertices[n*indices[3*i]+1]
|
||||
sx0 := vertices[n*indices[3*i]+2]
|
||||
sy0 := vertices[n*indices[3*i]+3]
|
||||
dx1 := vertices[n*indices[3*i+1]+0]
|
||||
dy1 := vertices[n*indices[3*i+1]+1]
|
||||
sx1 := vertices[n*indices[3*i+1]+2]
|
||||
sy1 := vertices[n*indices[3*i+1]+3]
|
||||
dx2 := vertices[n*indices[3*i+2]+0]
|
||||
dy2 := vertices[n*indices[3*i+2]+1]
|
||||
sx2 := vertices[n*indices[3*i+2]+2]
|
||||
sy2 := vertices[n*indices[3*i+2]+3]
|
||||
if l := mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1); level > l {
|
||||
level = l
|
||||
}
|
||||
if l := mipmapLevelFromDistance(dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2); level > l {
|
||||
level = l
|
||||
}
|
||||
if l := mipmapLevelFromDistance(dx2, dy2, dx0, dy0, sx2, sy2, sx0, sy0); level > l {
|
||||
level = l
|
||||
}
|
||||
}
|
||||
if level == math.MaxInt32 {
|
||||
panic("mipmap: level must be calculated at least once but not")
|
||||
}
|
||||
}
|
||||
|
||||
var dstImgs [graphics.ShaderDstImageCount]*buffered.Image
|
||||
for i, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
dstImgs[i] = dst.orig
|
||||
}
|
||||
|
||||
var srcImgs [graphics.ShaderSrcImageCount]*buffered.Image
|
||||
for i, src := range srcs {
|
||||
if src == nil {
|
||||
continue
|
||||
}
|
||||
if level != 0 {
|
||||
if img := src.level(level); img != nil {
|
||||
const n = graphics.VertexFloatCount
|
||||
s := float32(pow2(level))
|
||||
for i := 0; i < len(vertices)/n; i++ {
|
||||
vertices[i*n+2] /= s
|
||||
vertices[i*n+3] /= s
|
||||
}
|
||||
srcImgs[i] = img
|
||||
continue
|
||||
}
|
||||
}
|
||||
srcImgs[i] = src.orig
|
||||
}
|
||||
|
||||
buffered.DrawTrianglesMRT(dstImgs, srcImgs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule)
|
||||
for _, dst := range dsts {
|
||||
if dst == nil {
|
||||
continue
|
||||
}
|
||||
dst.deallocateMipmaps()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mipmap) setImg(level int, img *buffered.Image) {
|
||||
if m.imgs == nil {
|
||||
m.imgs = map[int]*buffered.Image{}
|
||||
@ -187,7 +262,7 @@ func (m *Mipmap) level(level int) *buffered.Image {
|
||||
s := buffered.NewImage(w2, h2, m.imageType)
|
||||
|
||||
dstRegion := image.Rect(0, 0, w2, h2)
|
||||
s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillAll)
|
||||
s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillRuleFillAll)
|
||||
m.setImg(level, s)
|
||||
|
||||
return m.imgs[level]
|
||||
|
@ -23,13 +23,13 @@ import (
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
exec "golang.org/x/sys/execabs"
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
|
@ -20,14 +20,13 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
exec "golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
func isWSL() (bool, error) {
|
||||
|
4
internal/processtest/testdata/issue2138.go
vendored
4
internal/processtest/testdata/issue2138.go
vendored
@ -59,7 +59,7 @@ func (g *Game) Update() error {
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
// Before the fix, some complex renderings with EvenOdd might cause a DirectX error like this (#2138):
|
||||
// Before the fix, some complex renderings with FillRuleEvenOdd might cause a DirectX error like this (#2138):
|
||||
// panic: directx: IDXGISwapChain4::Present failed: HRESULT(2289696773)
|
||||
|
||||
screen.DrawImage(debugCircleImage, nil)
|
||||
@ -74,7 +74,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
p.Arc(100, 100, 6, 0, 2*math.Pi, vector.Clockwise)
|
||||
filling, indicies := p.AppendVerticesAndIndicesForFilling(nil, nil)
|
||||
screen.DrawTriangles(filling, indicies, whiteTextureImage, &ebiten.DrawTrianglesOptions{
|
||||
FillRule: ebiten.EvenOdd,
|
||||
FillRule: ebiten.FillRuleEvenOdd,
|
||||
})
|
||||
}
|
||||
|
||||
|
74
internal/processtest/testdata/shader.go
vendored
Normal file
74
internal/processtest/testdata/shader.go
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright 2024 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
// This test confirms that deallocation of a shader works correctly.
|
||||
|
||||
type Game struct {
|
||||
count int
|
||||
|
||||
img *ebiten.Image
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
if g.img == nil {
|
||||
g.img = ebiten.NewImage(1, 1)
|
||||
}
|
||||
|
||||
g.count++
|
||||
|
||||
s, err := ebiten.NewShader([]byte(fmt.Sprintf(`//kage:unit pixels
|
||||
|
||||
package main
|
||||
|
||||
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
return vec4(%d/255.0)
|
||||
}
|
||||
`, g.count)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use the shader to ensure that the shader is actually allocated.
|
||||
g.img.DrawRectShader(1, 1, s, nil)
|
||||
|
||||
s.Deallocate()
|
||||
|
||||
if g.count == 60 {
|
||||
return ebiten.Termination
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
}
|
||||
|
||||
func (g *Game) Layout(w, h int) (int, int) {
|
||||
return 320, 240
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := ebiten.RunGame(&Game{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -199,7 +199,7 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
for _, expr := range es {
|
||||
if expr.Type == shaderir.FunctionExpr {
|
||||
if expr.Type == shaderir.FunctionExpr || expr.Type == shaderir.BuiltinFuncExpr {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("function name cannot be an argument: %s", e.Fun))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
@ -202,6 +202,7 @@ func Compile(src []byte, vertexEntry, fragmentEntry string, textureCount int) (*
|
||||
fragmentEntry: fragmentEntry,
|
||||
unit: unit,
|
||||
}
|
||||
s.ir.SourceHash = shaderir.CalcSourceHash(src)
|
||||
s.global.ir = &shaderir.Block{}
|
||||
s.parse(f)
|
||||
|
||||
@ -215,6 +216,7 @@ func Compile(src []byte, vertexEntry, fragmentEntry string, textureCount int) (*
|
||||
// TODO: Make a call graph and reorder the elements.
|
||||
|
||||
s.ir.TextureCount = textureCount
|
||||
|
||||
return &s.ir, nil
|
||||
}
|
||||
|
||||
@ -741,8 +743,9 @@ func (cs *compileState) parseFuncParams(block *block, fname string, d *ast.FuncD
|
||||
}
|
||||
|
||||
// If there is only one returning value, it is treated as a returning value.
|
||||
// Only if not the fragment entrypoint.
|
||||
// An array cannot be a returning value, especially for HLSL (#2923).
|
||||
if len(out) == 1 && out[0].name == "" && out[0].typ.Main != shaderir.Array {
|
||||
if fname != cs.fragmentEntry && len(out) == 1 && out[0].name == "" && out[0].typ.Main != shaderir.Array {
|
||||
ret = out[0].typ
|
||||
out = nil
|
||||
}
|
||||
@ -820,10 +823,15 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool
|
||||
return function{}, false
|
||||
}
|
||||
|
||||
if len(outParams) != 0 || returnType.Main != shaderir.Vec4 {
|
||||
cs.addError(d.Pos(), "fragment entry point must have one returning vec4 value for a color")
|
||||
return function{}, false
|
||||
// The first out-param is treated as fragColor0 in GLSL.
|
||||
for i := range outParams {
|
||||
if outParams[i].typ.Main != shaderir.Vec4 {
|
||||
cs.addError(d.Pos(), "fragment entry point must only have vec4 return values for colors")
|
||||
return function{}, false
|
||||
}
|
||||
}
|
||||
// Adjust the number of textures to write to
|
||||
cs.ir.ColorsOutCount = len(outParams)
|
||||
|
||||
if cs.varyingParsed {
|
||||
checkVaryings(inParams[1:])
|
||||
|
@ -188,13 +188,13 @@ func TestCompile(t *testing.T) {
|
||||
}
|
||||
|
||||
if tc.HLSL != nil {
|
||||
vs, _, _ := hlsl.Compile(s)
|
||||
vs, _ := hlsl.Compile(s)
|
||||
if got, want := hlslNormalize(vs), hlslNormalize(string(tc.HLSL)); got != want {
|
||||
compare(t, "HLSL", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if tc.Metal != nil {
|
||||
/*if tc.Metal != nil {
|
||||
m := msl.Compile(s)
|
||||
if got, want := metalNormalize(m), metalNormalize(string(tc.Metal)); got != want {
|
||||
compare(t, "Metal", got, want)
|
||||
@ -203,7 +203,7 @@ func TestCompile(t *testing.T) {
|
||||
|
||||
// Just check that Compile doesn't cause panic.
|
||||
// TODO: Should the results be tested?
|
||||
msl.Compile(s)
|
||||
msl.Compile(s)*/
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +334,8 @@ func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inP
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
if len(stmt.Results) != len(outParams) && len(stmt.Results) != 1 {
|
||||
if !(len(stmt.Results) == 0 && len(outParams) > 0 && outParams[0].name != "") {
|
||||
// Fragment function does not have to return a value due to discard
|
||||
if fname != cs.fragmentEntry && !(len(stmt.Results) == 0 && len(outParams) > 0 && outParams[0].name != "") {
|
||||
// TODO: Check variable shadowings.
|
||||
// https://go.dev/ref/spec#Return_statements
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("the number of returning variables must be %d but %d", len(outParams), len(stmt.Results)))
|
||||
|
@ -1897,6 +1897,16 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
Foo(Bar())
|
||||
return color
|
||||
}
|
||||
`)); err == nil {
|
||||
t.Errorf("error must be non-nil but was nil")
|
||||
}
|
||||
// Issue #2965
|
||||
if _, err := compileToIR([]byte(`package main
|
||||
|
||||
func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 {
|
||||
abs(sign)
|
||||
return color
|
||||
}
|
||||
`)); err == nil {
|
||||
t.Errorf("error must be non-nil but was nil")
|
||||
}
|
||||
|
35
internal/shader/testdata/for5.expected.fs
vendored
35
internal/shader/testdata/for5.expected.fs
vendored
@ -3,31 +3,32 @@ uniform float U1;
|
||||
uniform float U2;
|
||||
|
||||
int F0(in int l0);
|
||||
vec4 F1(in vec4 l0);
|
||||
void F1(in vec4 l0, out vec4 l1);
|
||||
|
||||
int F0(in int l0) {
|
||||
return l0;
|
||||
}
|
||||
|
||||
vec4 F1(in vec4 l0) {
|
||||
int l1 = 0;
|
||||
int l3 = 0;
|
||||
l1 = 0;
|
||||
for (int l2 = 0; l2 < 10; l2++) {
|
||||
int l3 = 0;
|
||||
l3 = F0(l2);
|
||||
l1 = (l1) + (l3);
|
||||
for (int l4 = 0; l4 < 10; l4++) {
|
||||
int l5 = 0;
|
||||
l5 = F0(l4);
|
||||
l1 = (l1) + (l5);
|
||||
void F1(in vec4 l0, out vec4 l1) {
|
||||
int l2 = 0;
|
||||
int l4 = 0;
|
||||
l2 = 0;
|
||||
for (int l3 = 0; l3 < 10; l3++) {
|
||||
int l4 = 0;
|
||||
l4 = F0(l3);
|
||||
l2 = (l2) + (l4);
|
||||
for (int l5 = 0; l5 < 10; l5++) {
|
||||
int l6 = 0;
|
||||
l6 = F0(l5);
|
||||
l2 = (l2) + (l6);
|
||||
}
|
||||
}
|
||||
l3 = 0;
|
||||
l1 = (l1) + (l3);
|
||||
return vec4(float(l1));
|
||||
l4 = 0;
|
||||
l2 = (l2) + (l4);
|
||||
l1 = vec4(float(l2));
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
fragColor = F1(gl_FragCoord);
|
||||
F1(gl_FragCoord, gl_FragData[0]);
|
||||
}
|
||||
|
12
internal/shader/testdata/issue1238.expected.fs
vendored
12
internal/shader/testdata/issue1238.expected.fs
vendored
@ -1,12 +1,14 @@
|
||||
vec4 F0(in vec4 l0);
|
||||
void F0(in vec4 l0, out vec4 l1);
|
||||
|
||||
vec4 F0(in vec4 l0) {
|
||||
void F0(in vec4 l0, out vec4 l1) {
|
||||
if (true) {
|
||||
return l0;
|
||||
l1 = l0;
|
||||
return;
|
||||
}
|
||||
return l0;
|
||||
l1 = l0;
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
fragColor = F0(gl_FragCoord);
|
||||
F0(gl_FragCoord, gl_FragData[0]);
|
||||
}
|
||||
|
15
internal/shader/testdata/issue1245.expected.fs
vendored
15
internal/shader/testdata/issue1245.expected.fs
vendored
@ -1,13 +1,14 @@
|
||||
vec4 F0(in vec4 l0);
|
||||
void F0(in vec4 l0, out vec4 l1);
|
||||
|
||||
vec4 F0(in vec4 l0) {
|
||||
vec4 l1 = vec4(0);
|
||||
for (float l2 = 0.0; l2 < 4.0; l2++) {
|
||||
(l1).x = ((l1).x) + ((l2) * (1.0000000000e-02));
|
||||
void F0(in vec4 l0, out vec4 l1) {
|
||||
vec4 l2 = vec4(0);
|
||||
for (float l3 = 0.0; l3 < 4.0; l3++) {
|
||||
(l2).x = ((l2).x) + ((l3) * (1.0000000000e-02));
|
||||
}
|
||||
return l1;
|
||||
l1 = l2;
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
fragColor = F0(gl_FragCoord);
|
||||
F0(gl_FragCoord, gl_FragData[0]);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
void F2(void);
|
||||
void F3(void);
|
||||
vec4 F5(in vec4 l0);
|
||||
void F5(in vec4 l0, out vec4 l1);
|
||||
|
||||
void F2(void) {
|
||||
}
|
||||
@ -9,11 +9,12 @@ void F3(void) {
|
||||
F2();
|
||||
}
|
||||
|
||||
vec4 F5(in vec4 l0) {
|
||||
void F5(in vec4 l0, out vec4 l1) {
|
||||
F3();
|
||||
return vec4(0.0);
|
||||
l1 = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
fragColor = F5(gl_FragCoord);
|
||||
F5(gl_FragCoord, gl_FragData[0]);
|
||||
}
|
||||
|
@ -2,12 +2,13 @@ uniform vec2 U0;
|
||||
in vec2 V0;
|
||||
in vec4 V1;
|
||||
|
||||
vec4 F0(in vec4 l0, in vec2 l1, in vec4 l2);
|
||||
void F0(in vec4 l0, in vec2 l1, in vec4 l2, out vec4 l3);
|
||||
|
||||
vec4 F0(in vec4 l0, in vec2 l1, in vec4 l2) {
|
||||
return vec4((l0).x, (l1).y, (l2).z, 1.0);
|
||||
void F0(in vec4 l0, in vec2 l1, in vec4 l2, out vec4 l3) {
|
||||
l3 = vec4((l0).x, (l1).y, (l2).z, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
fragColor = F0(gl_FragCoord, V0, V1);
|
||||
F0(gl_FragCoord, V0, V1, gl_FragData[0]);
|
||||
}
|
||||
|
@ -86,9 +86,7 @@ precision highp int;
|
||||
#define lowp
|
||||
#define mediump
|
||||
#define highp
|
||||
#endif
|
||||
|
||||
out vec4 fragColor;`
|
||||
#endif`
|
||||
if version == GLSLVersionDefault {
|
||||
prelude += "\n\n" + utilFunctions
|
||||
}
|
||||
@ -231,6 +229,12 @@ func Compile(p *shaderir.Program, version GLSLVersion) (vertexShader, fragmentSh
|
||||
fslines = append(fslines, fmt.Sprintf("in %s;", c.varDecl(p, &t, fmt.Sprintf("V%d", i))))
|
||||
}
|
||||
}
|
||||
// If ES300 out colors need to be defined explicitely
|
||||
if version == GLSLVersionES300 {
|
||||
for i := 0; i < p.ColorsOutCount; i++ {
|
||||
fslines = append(fslines, fmt.Sprintf("layout(location = %d) out vec4 glFragColor%d;", i, i))
|
||||
}
|
||||
}
|
||||
|
||||
var funcs []*shaderir.Func
|
||||
if p.VertexFunc.Block != nil {
|
||||
@ -420,7 +424,10 @@ func (c *compileContext) localVariableName(p *shaderir.Program, topBlock *shader
|
||||
case idx < nv+1:
|
||||
return fmt.Sprintf("V%d", idx-1)
|
||||
default:
|
||||
return fmt.Sprintf("l%d", idx-(nv+1))
|
||||
if c.version == GLSLVersionES300 {
|
||||
return fmt.Sprintf("glFragColor%d", idx-(nv+1))
|
||||
}
|
||||
return fmt.Sprintf("gl_FragData[%d]", idx-(nv+1))
|
||||
}
|
||||
default:
|
||||
return fmt.Sprintf("l%d", idx)
|
||||
@ -595,7 +602,7 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
case shaderir.Return:
|
||||
switch {
|
||||
case topBlock == p.FragmentFunc.Block:
|
||||
lines = append(lines, fmt.Sprintf("%sfragColor = %s;", idt, expr(&s.Exprs[0])))
|
||||
lines = append(lines, fmt.Sprintf("%s%s;", idt, expr(&s.Exprs[0])))
|
||||
// The 'return' statement is not required so far, as the fragment entrypoint has only one sentence so far. See adjustProgram implementation.
|
||||
case len(s.Exprs) == 0:
|
||||
lines = append(lines, idt+"return;")
|
||||
@ -604,7 +611,7 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
}
|
||||
case shaderir.Discard:
|
||||
// 'discard' is invoked only in the fragment shader entry point.
|
||||
lines = append(lines, idt+"discard;", idt+"return vec4(0.0);")
|
||||
lines = append(lines, idt+"discard;") //, idt+"return vec4(0.0);")
|
||||
default:
|
||||
lines = append(lines, fmt.Sprintf("%s?(unexpected stmt: %d)", idt, s.Type))
|
||||
}
|
||||
@ -645,15 +652,20 @@ func adjustProgram(p *shaderir.Program) *shaderir.Program {
|
||||
Main: shaderir.Vec4, // gl_FragCoord
|
||||
}
|
||||
copy(inParams[1:], newP.Varyings)
|
||||
// Out parameters of a fragment func are colors
|
||||
outParams := make([]shaderir.Type, p.ColorsOutCount)
|
||||
for i := range outParams {
|
||||
outParams[i] = shaderir.Type{
|
||||
Main: shaderir.Vec4,
|
||||
}
|
||||
}
|
||||
newP.FragmentFunc.Block.LocalVarIndexOffset += (p.ColorsOutCount - 1)
|
||||
|
||||
newP.Funcs = append(newP.Funcs, shaderir.Func{
|
||||
Index: funcIdx,
|
||||
InParams: inParams,
|
||||
OutParams: nil,
|
||||
Return: shaderir.Type{
|
||||
Main: shaderir.Vec4,
|
||||
},
|
||||
Block: newP.FragmentFunc.Block,
|
||||
OutParams: outParams,
|
||||
Block: newP.FragmentFunc.Block,
|
||||
})
|
||||
|
||||
// Create an AST to call the new function.
|
||||
@ -663,7 +675,7 @@ func adjustProgram(p *shaderir.Program) *shaderir.Program {
|
||||
Index: funcIdx,
|
||||
},
|
||||
}
|
||||
for i := 0; i < 1+len(newP.Varyings); i++ {
|
||||
for i := 0; i < 1+len(newP.Varyings)+p.ColorsOutCount; i++ {
|
||||
call = append(call, shaderir.Expr{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: i,
|
||||
|
@ -86,8 +86,9 @@ float4x4 float4x4FromScalar(float x) {
|
||||
return float4x4(x, 0, 0, 0, 0, x, 0, 0, 0, 0, x, 0, 0, 0, 0, x);
|
||||
}`
|
||||
|
||||
func Compile(p *shaderir.Program) (vertexShader, pixelShader string, offsets []int) {
|
||||
offsets = calculateMemoryOffsets(p.Uniforms)
|
||||
func Compile(p *shaderir.Program) (vertexShader, pixelShader string) {
|
||||
offsets := CalcUniformMemoryOffsets(p)
|
||||
p = adjustProgram(p)
|
||||
|
||||
c := &compileContext{
|
||||
unit: p.Unit,
|
||||
@ -190,7 +191,15 @@ func Compile(p *shaderir.Program) (vertexShader, pixelShader string, offsets []i
|
||||
}
|
||||
if p.FragmentFunc.Block != nil && len(p.FragmentFunc.Block.Stmts) > 0 {
|
||||
pslines = append(pslines, "")
|
||||
pslines = append(pslines, fmt.Sprintf("float4 PSMain(Varyings %s) : SV_TARGET {", vsOut))
|
||||
pslines = append(pslines, "struct PS_OUTPUT")
|
||||
pslines = append(pslines, "{")
|
||||
for i := 0; i < p.ColorsOutCount; i++ {
|
||||
pslines = append(pslines, fmt.Sprintf("\tfloat4 Color%d: SV_Target%d;", i, i))
|
||||
}
|
||||
pslines = append(pslines, "};")
|
||||
pslines = append(pslines, "")
|
||||
pslines = append(pslines, fmt.Sprintf("PS_OUTPUT PSMain(Varyings %s) {", vsOut))
|
||||
pslines = append(pslines, "\tPS_OUTPUT output;")
|
||||
pslines = append(pslines, c.block(p, p.FragmentFunc.Block, p.FragmentFunc.Block, 0)...)
|
||||
pslines = append(pslines, "}")
|
||||
}
|
||||
@ -353,7 +362,7 @@ func (c *compileContext) localVariableName(p *shaderir.Program, topBlock *shader
|
||||
case idx < nv+1:
|
||||
return fmt.Sprintf("%s.M%d", vsOut, idx-1)
|
||||
default:
|
||||
return fmt.Sprintf("l%d", idx-(nv+1))
|
||||
return fmt.Sprintf("output.Color%d", idx-(nv+1))
|
||||
}
|
||||
default:
|
||||
return fmt.Sprintf("l%d", idx)
|
||||
@ -563,6 +572,10 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
switch {
|
||||
case topBlock == p.VertexFunc.Block:
|
||||
lines = append(lines, fmt.Sprintf("%sreturn %s;", idt, vsOut))
|
||||
case topBlock == p.FragmentFunc.Block:
|
||||
// Call to the pseudo fragment func based on out parameters
|
||||
lines = append(lines, idt+expr(&s.Exprs[0])+";")
|
||||
lines = append(lines, idt+"return output;")
|
||||
case len(s.Exprs) == 0:
|
||||
lines = append(lines, idt+"return;")
|
||||
default:
|
||||
@ -570,7 +583,7 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
}
|
||||
case shaderir.Discard:
|
||||
// 'discard' is invoked only in the fragment shader entry point.
|
||||
lines = append(lines, idt+"discard;", idt+"return float4(0.0, 0.0, 0.0, 0.0);")
|
||||
lines = append(lines, idt+"discard;")
|
||||
default:
|
||||
lines = append(lines, fmt.Sprintf("%s?(unexpected stmt: %d)", idt, s.Type))
|
||||
}
|
||||
@ -578,3 +591,86 @@ func (c *compileContext) block(p *shaderir.Program, topBlock, block *shaderir.Bl
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func adjustProgram(p *shaderir.Program) *shaderir.Program {
|
||||
if p.FragmentFunc.Block == nil {
|
||||
return p
|
||||
}
|
||||
|
||||
// Shallow-clone the program in order not to modify p itself.
|
||||
newP := *p
|
||||
|
||||
// Create a new slice not to affect the original p.
|
||||
newP.Funcs = make([]shaderir.Func, len(p.Funcs))
|
||||
copy(newP.Funcs, p.Funcs)
|
||||
|
||||
// Create a new function whose body is the same is the fragment shader's entry point.
|
||||
// Determine a unique index of the new function.
|
||||
var funcIdx int
|
||||
for _, f := range newP.Funcs {
|
||||
if funcIdx <= f.Index {
|
||||
funcIdx = f.Index + 1
|
||||
}
|
||||
}
|
||||
|
||||
// For parameters of a fragment func, see the comment in internal/shaderir/program.go.
|
||||
inParams := make([]shaderir.Type, 1+len(newP.Varyings))
|
||||
inParams[0] = shaderir.Type{
|
||||
Main: shaderir.Vec4, // gl_FragCoord
|
||||
}
|
||||
copy(inParams[1:], newP.Varyings)
|
||||
// Out parameters of a fragment func are colors
|
||||
outParams := make([]shaderir.Type, p.ColorsOutCount)
|
||||
for i := range outParams {
|
||||
outParams[i] = shaderir.Type{
|
||||
Main: shaderir.Vec4,
|
||||
}
|
||||
}
|
||||
newP.FragmentFunc.Block.LocalVarIndexOffset += (p.ColorsOutCount - 1)
|
||||
|
||||
newP.Funcs = append(newP.Funcs, shaderir.Func{
|
||||
Index: funcIdx,
|
||||
InParams: inParams,
|
||||
OutParams: outParams,
|
||||
Block: newP.FragmentFunc.Block,
|
||||
})
|
||||
|
||||
// Create an AST to call the new function.
|
||||
call := []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.FunctionExpr,
|
||||
Index: funcIdx,
|
||||
},
|
||||
}
|
||||
for i := 0; i < 1+len(newP.Varyings)+p.ColorsOutCount; i++ {
|
||||
call = append(call, shaderir.Expr{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: i,
|
||||
})
|
||||
}
|
||||
|
||||
// Replace the entry point with just calling the new function.
|
||||
stmts := []shaderir.Stmt{
|
||||
{
|
||||
// Return: This will be replaced with a call to the new function.
|
||||
// Then the output structure containing colors will be returned.
|
||||
Type: shaderir.Return,
|
||||
Exprs: []shaderir.Expr{
|
||||
// The function call
|
||||
{
|
||||
Type: shaderir.Call,
|
||||
Exprs: call,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
newP.FragmentFunc = shaderir.FragmentFunc{
|
||||
Block: &shaderir.Block{
|
||||
LocalVars: nil,
|
||||
LocalVarIndexOffset: 1 + len(newP.Varyings) + 1,
|
||||
Stmts: stmts,
|
||||
},
|
||||
}
|
||||
|
||||
return &newP
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
|
||||
const boundaryInBytes = 16
|
||||
|
||||
func calculateMemoryOffsets(uniforms []shaderir.Type) []int {
|
||||
func CalcUniformMemoryOffsets(program *shaderir.Program) []int {
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
|
||||
// https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing
|
||||
|
||||
@ -38,7 +38,7 @@ func calculateMemoryOffsets(uniforms []shaderir.Type) []int {
|
||||
|
||||
// TODO: Reorder the variables with packoffset.
|
||||
// See https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-packoffset
|
||||
for _, u := range uniforms {
|
||||
for _, u := range program.Uniforms {
|
||||
switch u.Main {
|
||||
case shaderir.Float:
|
||||
offsets = append(offsets, head)
|
||||
|
@ -938,12 +938,12 @@ void F0(float l0, float l1, thread float& l2) {
|
||||
},
|
||||
Attributes: []shaderir.Type{
|
||||
{Main: shaderir.Vec4},
|
||||
{Main: shaderir.Float},
|
||||
{Main: shaderir.Vec2},
|
||||
{Main: shaderir.Vec4},
|
||||
},
|
||||
Varyings: []shaderir.Type{
|
||||
{Main: shaderir.Float},
|
||||
{Main: shaderir.Vec2},
|
||||
{Main: shaderir.Vec4},
|
||||
},
|
||||
VertexFunc: shaderir.VertexFunc{
|
||||
Block: block(
|
||||
@ -967,10 +967,10 @@ void F0(float l0, float l1, thread float& l2) {
|
||||
GlslVS: glslVertexPrelude + `
|
||||
uniform float U0;
|
||||
in vec4 A0;
|
||||
in float A1;
|
||||
in vec2 A2;
|
||||
out float V0;
|
||||
out vec2 V1;
|
||||
in vec2 A1;
|
||||
in vec4 A2;
|
||||
out vec2 V0;
|
||||
out vec4 V1;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = A0;
|
||||
@ -979,8 +979,8 @@ void main(void) {
|
||||
}`,
|
||||
GlslFS: glslFragmentPrelude + `
|
||||
uniform float U0;
|
||||
in float V0;
|
||||
in vec2 V1;`,
|
||||
in vec2 V0;
|
||||
in vec4 V1;`,
|
||||
},
|
||||
{
|
||||
Name: "FragmentFunc",
|
||||
@ -991,12 +991,12 @@ in vec2 V1;`,
|
||||
},
|
||||
Attributes: []shaderir.Type{
|
||||
{Main: shaderir.Vec4},
|
||||
{Main: shaderir.Float},
|
||||
{Main: shaderir.Vec2},
|
||||
{Main: shaderir.Vec4},
|
||||
},
|
||||
Varyings: []shaderir.Type{
|
||||
{Main: shaderir.Float},
|
||||
{Main: shaderir.Vec2},
|
||||
{Main: shaderir.Vec4},
|
||||
},
|
||||
VertexFunc: shaderir.VertexFunc{
|
||||
Block: block(
|
||||
@ -1016,39 +1016,39 @@ in vec2 V1;`,
|
||||
),
|
||||
),
|
||||
},
|
||||
ColorsOutCount: 1,
|
||||
FragmentFunc: shaderir.FragmentFunc{
|
||||
Block: block(
|
||||
[]shaderir.Type{
|
||||
{Main: shaderir.Vec2},
|
||||
{Main: shaderir.Vec4},
|
||||
{Main: shaderir.Float},
|
||||
},
|
||||
3,
|
||||
assignStmt(
|
||||
localVariableExpr(3),
|
||||
localVariableExpr(0),
|
||||
),
|
||||
3+1,
|
||||
assignStmt(
|
||||
localVariableExpr(4),
|
||||
localVariableExpr(1),
|
||||
),
|
||||
returnStmt(
|
||||
callExpr(
|
||||
builtinFuncExpr(shaderir.Vec4F),
|
||||
localVariableExpr(2),
|
||||
localVariableExpr(1),
|
||||
localVariableExpr(1),
|
||||
),
|
||||
assignStmt(
|
||||
localVariableExpr(5),
|
||||
localVariableExpr(2),
|
||||
),
|
||||
assignStmt(
|
||||
localVariableExpr(3),
|
||||
localVariableExpr(0),
|
||||
),
|
||||
shaderir.Stmt{
|
||||
Type: shaderir.Return,
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
GlslVS: glslVertexPrelude + `
|
||||
uniform float U0;
|
||||
in vec4 A0;
|
||||
in float A1;
|
||||
in vec2 A2;
|
||||
out float V0;
|
||||
out vec2 V1;
|
||||
in vec2 A1;
|
||||
in vec4 A2;
|
||||
out vec2 V0;
|
||||
out vec4 V1;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = A0;
|
||||
@ -1057,21 +1057,22 @@ void main(void) {
|
||||
}`,
|
||||
GlslFS: glslFragmentPrelude + `
|
||||
uniform float U0;
|
||||
in float V0;
|
||||
in vec2 V1;
|
||||
in vec2 V0;
|
||||
in vec4 V1;
|
||||
|
||||
vec4 F0(in vec4 l0, in float l1, in vec2 l2);
|
||||
void F0(in vec4 l0, in vec2 l1, in vec4 l2, out vec4 l3);
|
||||
|
||||
vec4 F0(in vec4 l0, in float l1, in vec2 l2) {
|
||||
vec4 l3 = vec4(0);
|
||||
float l4 = float(0);
|
||||
l3 = l0;
|
||||
void F0(in vec4 l0, in vec2 l1, in vec4 l2, out vec4 l3) {
|
||||
vec2 l4 = vec2(0);
|
||||
vec4 l5 = vec4(0);
|
||||
l4 = l1;
|
||||
return vec4(l2, l1, l1);
|
||||
l5 = l2;
|
||||
l3 = l0;
|
||||
return;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
fragColor = F0(gl_FragCoord, V0, V1);
|
||||
F0(gl_FragCoord, V0, V1, gl_FragData[0]);
|
||||
}`,
|
||||
},
|
||||
}
|
||||
@ -1093,14 +1094,14 @@ void main(void) {
|
||||
t.Errorf("%s fragment: got: %s, want: %s", tc.Name, got, want)
|
||||
}
|
||||
}
|
||||
m := msl.Compile(&tc.Program)
|
||||
/*m := msl.Compile(&tc.Program)
|
||||
if tc.Metal != "" {
|
||||
got := m
|
||||
want := tc.Metal + "\n"
|
||||
if got != want {
|
||||
t.Errorf("%s metal: got: %s, want: %s", tc.Name, got, want)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,10 @@
|
||||
package shaderir
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"hash/fnv"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
@ -29,16 +31,34 @@ const (
|
||||
Pixels
|
||||
)
|
||||
|
||||
type SourceHash [16]byte
|
||||
|
||||
func CalcSourceHash(source []byte) SourceHash {
|
||||
h := fnv.New128a()
|
||||
_, _ = h.Write(source)
|
||||
|
||||
var hash SourceHash
|
||||
h.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
||||
func (s SourceHash) String() string {
|
||||
return hex.EncodeToString(s[:])
|
||||
}
|
||||
|
||||
type Program struct {
|
||||
UniformNames []string
|
||||
Uniforms []Type
|
||||
TextureCount int
|
||||
Attributes []Type
|
||||
Varyings []Type
|
||||
Funcs []Func
|
||||
VertexFunc VertexFunc
|
||||
FragmentFunc FragmentFunc
|
||||
Unit Unit
|
||||
UniformNames []string
|
||||
Uniforms []Type
|
||||
TextureCount int
|
||||
ColorsOutCount int
|
||||
Attributes []Type
|
||||
Varyings []Type
|
||||
Funcs []Func
|
||||
VertexFunc VertexFunc
|
||||
FragmentFunc FragmentFunc
|
||||
Unit Unit
|
||||
|
||||
SourceHash SourceHash
|
||||
|
||||
uniformFactors []uint32
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user