patchupdate.py: Various speed optimizations and improvements, removed several patches (accepted upstream).

This commit is contained in:
Sebastian Lackner 2014-11-29 08:17:16 +01:00
parent e6d0cbe554
commit db0fd400e4
11 changed files with 358 additions and 1272 deletions

3
.gitignore vendored
View File

@ -1,5 +1,4 @@
debian/tools/wine
.depcache
.srccache
.patchupdate.cache
*.ok
*.pyc

View File

@ -39,13 +39,12 @@ Wine. All those differences are also documented on the
Included bug fixes and improvements
===================================
**Bugfixes and features included in the next upcoming release [6]:**
**Bugfixes and features included in the next upcoming release [5]:**
* Avoid race-conditions of async WSARecv() operations with write watches.
* Black & White needs DXTn software decoding support ([Wine Bug #14939](https://bugs.winehq.org/show_bug.cgi?id=14939))
* Fix issues with dragging layers between images in Adobe Photoshop 7.0 ([Wine Bug #12007](https://bugs.winehq.org/show_bug.cgi?id=12007))
* Implement exclusive mode in PulseAudio backend ([Wine Bug #37042](https://bugs.winehq.org/show_bug.cgi?id=37042))
* Take abs() of vertex z coordinate as FFP fog coordinate
* Wintrust doesn't reset data->pWintrustData->u.pFile->hFile after closing handle ([Wine Bug #36257](https://bugs.winehq.org/show_bug.cgi?id=36257))

3
debian/changelog vendored
View File

@ -1,4 +1,5 @@
wine-compholio (1.7.32) UNRELEASED; urgency=low
* Various optimizations of patchupdate.py.
* Update patch for SO_CONNECT_TIME and adding better tests.
* Update patch for FD Cache and use faster method on x86_64.
* Added patch to ensure dbghelp always checks for debug symbols in BINDIR.
@ -19,6 +20,8 @@ wine-compholio (1.7.32) UNRELEASED; urgency=low
* Removed patches to avoid sending messages in FindWindowExW (accepted upstream).
* Removed patch to fix implementation ofCoWaitForMultipleHandles (accepted upstream).
* Removed patch to avoid interthread no-op messages in ShowWindow (accepted upstream).
* Removed patches to take abs() of vertex z coordinate as FFP fog coordinate (fixed upstream).
* Removed patch to fix detection of gnutls on Ubuntu 14.10.
-- Sebastian Lackner <sebastian@fds-team.de> Sat, 15 Nov 2014 20:21:53 +0100
wine-compholio (1.7.31) unstable; urgency=low

View File

@ -19,24 +19,31 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
import binascii
import cPickle as pickle
import hashlib
import itertools
import math
import multiprocessing.pool
import operator
import os
import patchutils
import pickle
import progressbar
import re
import signal
import subprocess
import sys
import tempfile
import textwrap
_devnull = open(os.devnull, 'wb')
# Cached information to speed up patch dependency checks
latest_wine_commit = None
cached_patch_result = {}
cached_original_src = {}
class config(object):
path_depcache = ".depcache"
path_srccache = ".srccache"
path_depcache = ".patchupdate.cache"
path_patches = "patches"
path_changelog = "debian/changelog"
@ -82,6 +89,22 @@ def _unique(iterable, key=None):
# _unique('ABBCcAD', str.lower) --> A B C A D
return itertools.imap(next, itertools.imap(operator.itemgetter(1), itertools.groupby(iterable, key)))
def _split_seq(iterable, size):
"""Split an iterator into chunks of a given size."""
it = iter(iterable)
items = list(itertools.islice(it, size))
while items:
yield items
items = list(itertools.islice(it, size))
def _merge_seq(iterable, callback=None):
"""Merge lists/iterators into a new one. Call callback after each chunk"""
for i, items in enumerate(iterable):
if callback is not None:
callback(i)
for obj in items:
yield obj
def _escape(s):
"""Escape string inside of '...' quotes."""
return s.replace("\\", "\\\\\\\\").replace("\"", "\\\"").replace("'", "'\\''")
@ -99,7 +122,17 @@ def _save_dict(filename, value):
with open(filename, "wb") as fp:
pickle.dump(value, fp, pickle.HIGHEST_PROTOCOL)
def parse_int(val, default=0):
def _sha256(fp):
"""Calculate sha256sum from a file descriptor."""
m = hashlib.sha256()
fp.seek(0)
while True:
buf = fp.read(16384)
if buf == "": break
m.update(buf)
return m.digest()
def _parse_int(val, default=0):
"""Parse an integer or boolean value."""
r = re.match("^[0-9]+$", val)
if r:
@ -109,21 +142,21 @@ def parse_int(val, default=0):
except AttributeError:
return default
# Read information from changelog
def _read_changelog():
"""Read information from changelog."""
with open(config.path_changelog) as fp:
for line in fp:
r = re.match("^([a-zA-Z0-9][^(]*)\((.*)\) ([^;]*)", line)
if r: yield (r.group(1).strip(), r.group(2).strip(), r.group(3).strip())
# Get version number of the latest stable release
def _stable_compholio_version():
"""Get version number of the latest stable release."""
for package, version, distro in _read_changelog():
if distro.lower() != "unreleased":
return version
# Get latest wine commit
def _latest_wine_commit():
"""Get latest wine commit."""
if not os.path.isdir(config.path_wine):
raise PatchUpdaterError("Please create a symlink to the wine repository in %s" % config.path_wine)
commit = subprocess.check_output(["git", "rev-parse", "origin/master"], cwd=config.path_wine).strip()
@ -149,8 +182,7 @@ def enum_directories(revision, path):
else:
filename = "%s:%s" % (revision, path)
try:
with open(os.devnull, 'w') as devnull:
content = subprocess.check_output(["git", "show", filename], stderr=devnull)
content = subprocess.check_output(["git", "show", filename], stderr=_devnull)
except subprocess.CalledProcessError as e:
if e.returncode != 128: raise
return [] # ignore error
@ -166,7 +198,6 @@ def enum_directories(revision, path):
def read_definition(revision, filename, name_to_id):
"""Read a definition file and return information as tuple (depends, fixes)."""
filename = os.path.join(filename, "definition")
if revision is None:
with open(filename) as fp:
@ -174,8 +205,7 @@ def read_definition(revision, filename, name_to_id):
else:
filename = "%s:%s" % (revision, filename)
try:
with open(os.devnull, 'w') as devnull:
content = subprocess.check_output(["git", "show", filename], stderr=devnull)
content = subprocess.check_output(["git", "show", filename], stderr=_devnull)
except subprocess.CalledProcessError:
raise IOError("Failed to load %s" % filename)
@ -186,18 +216,15 @@ def read_definition(revision, filename, name_to_id):
for line in content.split("\n"):
if line.startswith("#"):
continue
tmp = line.split(":", 1)
if len(tmp) != 2:
continue
key, val = tmp[0].lower(), tmp[1].strip()
if key == "depends":
if name_to_id is not None:
if not name_to_id.has_key(val):
raise PatchUpdaterError("Definition file %s references unknown dependency %s" % (filename, val))
depends.add(name_to_id[val])
elif key == "fixes":
r = re.match("^[0-9]+$", val)
if r:
@ -208,10 +235,8 @@ def read_definition(revision, filename, name_to_id):
fixes.append((int(r.group(1)), r.group(2).strip()))
continue
fixes.append((None, val))
elif key == "disabled":
disabled = parse_int(val)
disabled = _parse_int(val)
elif revision is None:
print "WARNING: Ignoring unknown command in definition file %s: %s" % (filename, line)
@ -219,7 +244,6 @@ def read_definition(revision, filename, name_to_id):
def read_patchset(revision = None):
"""Read information about all patchsets for a specific revision."""
unique_id = itertools.count()
all_patches = {}
name_to_id = {}
@ -285,15 +309,12 @@ def causal_time_relation_any(all_patches, indices):
return False
return True
def causal_time_permutations(all_patches, indices, filename):
def causal_time_permutations(all_patches, indices):
"""Iterate over all possible permutations of patches affecting
a specific file, which are compatible with dependencies."""
for permutation in itertools.permutations(indices):
if causal_time_relation(all_patches, permutation):
selected_patches = []
for i in permutation:
selected_patches += [patch for patch in all_patches[i].patches if patch.modified_file == filename]
yield selected_patches
yield permutation
def contains_binary_patch(all_patches, indices, filename):
"""Checks if any patch with given indices affecting filename is a binary patch."""
@ -303,30 +324,43 @@ def contains_binary_patch(all_patches, indices, filename):
return True
return False
def get_wine_file(filename, force=False):
def get_wine_file(filename):
"""Return the hash and optionally the content of a file."""
# If we're not forced, we try to save time and only lookup the cached checksum
entry = "%s:%s" % (latest_wine_commit, filename)
if not force and cached_original_src.has_key(entry):
return (cached_original_src[entry], None)
# Grab original file from the wine git repository
entry = "%s:%s" % (latest_wine_commit, filename)
result = tempfile.NamedTemporaryFile()
try:
with open(os.devnull, 'w') as devnull:
content = subprocess.check_output(["git", "show", entry], cwd=config.path_wine, stderr=devnull)
content = subprocess.check_call(["git", "show", entry], cwd=config.path_wine, \
stdout=result, stderr=_devnull)
except subprocess.CalledProcessError as e:
if e.returncode != 128: raise
content = ""
result.flush() # shouldn't be necessary because the subprocess writes directly to the fd
return result
content_hash = hashlib.sha256(content).digest()
cached_original_src[entry] = content_hash
return (content_hash, content)
def select_patches(all_patches, indices, filename):
"""Create a temporary patch file for each patchset and calculate the checksum."""
selected_patches = {}
def verify_patch_order(all_patches, indices, filename):
for i in indices:
p = tempfile.NamedTemporaryFile()
m = hashlib.sha256()
for patch in all_patches[i].patches:
if patch.modified_file != filename:
continue
for chunk in patch.read_chunks():
p.write(chunk)
m.update(chunk)
p.write("\n")
m.update("\n")
p.flush()
selected_patches[i] = (m.digest(), p)
return selected_patches
def verify_patch_order(all_patches, indices, filename, pool):
"""Checks if the dependencies are defined correctly by applying
the patches on a (temporary) copy from the git tree."""
global cached_patch_result
# If one of patches is a binary patch, then we cannot / won't verify it - require dependencies in this case
if contains_binary_patch(all_patches, indices, filename):
@ -335,55 +369,99 @@ def verify_patch_order(all_patches, indices, filename):
(filename, ", ".join([all_patches[i].name for i in indices])))
return
# Get at least the checksum of the original file
original_content_hash, original_content = get_wine_file(filename)
original_content = get_wine_file(filename)
original_content_hash = _sha256(original_content)
selected_patches = select_patches(all_patches, indices, filename)
try:
# Check for possible ways to apply the patch
failed_to_apply = False
last_result_hash = None
for patches in causal_time_permutations(all_patches, indices, filename):
# Calculate unique hash based on the original content and the order in which the patches are applied
m = hashlib.sha256()
m.update(original_content_hash)
for patch in patches:
m.update(patch.hash())
unique_hash = m.digest()
# Fast path -> we know that it applies properly
if cached_patch_result.has_key(unique_hash):
result_hash = cached_patch_result[unique_hash]
assert result_hash is not None
else:
# Now really get the file, if we don't have it yet
if original_content is None:
original_content_hash, original_content = get_wine_file(filename, force=True)
# Apply the patches (without fuzz)
def _test_apply(permutations):
"""Tests if specific permutations of patches apply on the wine source tree."""
patch_stack_indices = []
patch_stack_patches = []
try:
content = patchutils.apply_patch(original_content, patches, fuzz=0)
except patchutils.PatchApplyError:
# Remember that we failed to apply the patches, but continue, if there is still a chance
# that it applies in a different order (to give a better error message).
failed_to_apply = True
if last_result_hash is None:
continue
break
# Get hash of resulting file and add to cache
result_hash = hashlib.sha256(content).digest()
cached_patch_result[unique_hash] = result_hash
for permutation in permutations:
# No known hash yet, remember the result. If we failed applying before, we can stop now.
if last_result_hash is None:
last_result_hash = result_hash
if failed_to_apply: break
# Calculate hash
m = hashlib.sha256()
m.update(original_content_hash)
for i in permutation:
m.update(selected_patches[i][0])
input_hash = m.digest()
# Applied successful, but result has a different hash - also treat as failure.
elif last_result_hash != result_hash:
failed_to_apply = True
break
# Fast path -> we know that it applies properly
try:
yield cached_patch_result[input_hash]
continue
except KeyError:
pass
# Remove unneeded patches from patch stack
while list(permutation[:len(patch_stack_indices)]) != patch_stack_indices:
patch_stack_indices.pop()
patch_stack_patches.pop().close()
# Apply the patches (without fuzz)
try:
while len(patch_stack_indices) < len(permutation):
i = permutation[len(patch_stack_indices)]
original = patch_stack_patches[-1] if len(patch_stack_indices) else original_content
patchfile = selected_patches[i][1]
patch_stack_patches.append(patchutils.apply_patch(original, patchfile, fuzz=0))
patch_stack_indices.append(i)
output_hash = _sha256(patch_stack_patches[-1])
except patchutils.PatchApplyError:
output_hash = None
cached_patch_result[input_hash] = output_hash
yield output_hash
finally:
# Ensure temporary files are cleaned up properly
while len(patch_stack_patches):
patch_stack_patches.pop().close()
# Show a progress bar while applying the patches - this task might take some time
chunk_size = 20
total_tasks = (math.factorial(len(indices)) + chunk_size - 1) / chunk_size
with progressbar.ProgressBar(desc=filename, total=total_tasks) as progress:
failed_to_apply = False
last_result_hash = None
# Check for possible ways to apply the patch
it = _split_seq(causal_time_permutations(all_patches, indices), chunk_size)
for output_hash in _merge_seq(pool.imap_unordered(lambda seq: list(_test_apply(seq)), it), \
callback=progress.update):
# Failed to apply patch, continue checking the rest.
if output_hash is None:
failed_to_apply = True
if last_result_hash is None:
continue
break
# No known hash yet, remember the result. If we failed applying before, we can stop now.
elif last_result_hash is None:
last_result_hash = output_hash
if failed_to_apply: break
# Applied successful, but result has a different hash - also treat as failure.
elif last_result_hash != output_hash:
failed_to_apply = True
break
if failed_to_apply:
progress.finish("<failed to apply>")
elif verbose:
progress.finish(binascii.hexlify(last_result_hash))
finally:
original_content.close()
for _, (_, p) in selected_patches.iteritems():
p.close()
# If something failed, then show the appropriate error message.
if failed_to_apply and last_result_hash is None:
@ -403,14 +481,11 @@ def verify_dependencies(all_patches):
def _load_patch_cache():
"""Load dictionary for cached patch dependency tests."""
global cached_patch_result
global cached_original_src
cached_patch_result = _load_dict(config.path_depcache)
cached_original_src = _load_dict(config.path_srccache)
def _save_patch_cache():
"""Save dictionary for cached patch dependency tests."""
_save_dict(config.path_depcache, cached_patch_result)
_save_dict(config.path_srccache, cached_original_src)
_save_dict(config.path_depcache, cached_patch_result.copy())
enabled_patches = dict([(i, patch) for i, patch in all_patches.iteritems() if not patch.disabled])
max_patches = max(enabled_patches.keys()) + 1
@ -450,11 +525,13 @@ def verify_dependencies(all_patches):
# Check if patches always apply correctly
_load_patch_cache()
pool = multiprocessing.pool.ThreadPool(processes=8)
try:
for f, indices in modified_files.iteritems():
verify_patch_order(enabled_patches, indices, f)
verify_patch_order(enabled_patches, indices, f, pool)
finally:
_save_patch_cache()
pool.close()
def generate_makefile(all_patches):
"""Generate Makefile for a specific set of patches."""
@ -464,9 +541,9 @@ def generate_makefile(all_patches):
with open(config.path_Makefile, "w") as fp:
fp.write(template.format(patchlist="\t" + " \\\n\t".join(
["%s.ok" % patch.name for i, patch in all_patches.iteritems() if not patch.disabled])))
["%s.ok" % patch.name for _, patch in all_patches.iteritems() if not patch.disabled])))
for i, patch in all_patches.iteritems():
for _, patch in all_patches.iteritems():
fp.write("# Patchset %s\n" % patch.name)
fp.write("# |\n")
@ -513,13 +590,13 @@ def generate_markdown(all_patches, stable_patches, stable_compholio_version):
all_fixes = {}
# Get fixes for current version
for i, patch in all_patches.iteritems():
for _, patch in all_patches.iteritems():
for bugid, bugname in patch.fixes:
key = bugid if bugid is not None else bugname
all_fixes[key] = [1, bugid, bugname]
# Compare with fixes for latest stable version
for i, patch in stable_patches.iteritems():
for _, patch in stable_patches.iteritems():
for bugid, bugname in patch.fixes:
key = bugid if bugid is not None else bugname
if all_fixes.has_key(key):
@ -565,6 +642,14 @@ def generate_markdown(all_patches, stable_patches, stable_compholio_version):
fp.write(template.format(version=stable_compholio_version))
if __name__ == "__main__":
verbose = "-v" in sys.argv[1:]
# Hack to avoid KeyboardInterrupts on worker threads
def _sig_int(signum=None, frame=None):
signal.signal(signal.SIGINT, signal.SIG_IGN)
raise RuntimeError("CTRL+C pressed")
signal.signal(signal.SIGINT, _sig_int)
try:
# Get information about Wine and Compholio version

View File

@ -19,16 +19,19 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
import email.header
import collections
import difflib
import email.header
import hashlib
import itertools
import os
import re
import shutil
import subprocess
import tempfile
_devnull = open(os.devnull, 'wb')
class PatchParserError(RuntimeError):
"""Unable to parse patch file - either an unimplemented feature, or corrupted patch."""
pass
@ -44,7 +47,7 @@ class PatchObject(object):
self.patch_subject = header['subject']
self.patch_revision = header['revision'] if header.has_key('revision') else 1
self.extracted_patch = None
# self.extracted_patch = None
self.unique_hash = None
self.filename = filename
@ -70,76 +73,58 @@ class PatchObject(object):
fp.seek(self.offset_begin)
i = self.offset_end - self.offset_begin
while i > 0:
buf = fp.read(4096 if i > 4096 else i)
buf = fp.read(16384 if i > 16384 else i)
if buf == "": raise IOError("Unable to extract patch.")
yield buf
i -= len(buf)
def extract(self):
"""Create a temporary file containing the extracted patch."""
if not self.extracted_patch:
self.extracted_patch = tempfile.NamedTemporaryFile()
for chunk in self.read_chunks():
self.extracted_patch.write(chunk)
self.extracted_patch.flush()
return self.extracted_patch
class _FileReader(object):
def __init__(self, filename):
self.filename = filename
self.fp = open(self.filename)
self.peeked = None
def hash(self):
"""Hash the content of the patch."""
if not self.unique_hash:
m = hashlib.sha256()
for chunk in self.read_chunks():
m.update(chunk)
self.unique_hash = m.digest()
return self.unique_hash
def close(self):
self.fp.close()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
def seek(self, pos):
"""Change the file cursor position."""
self.fp.seek(pos)
self.peeked = None
def tell(self):
"""Return the current file cursor position."""
if self.peeked is None:
return self.fp.tell()
return self.peeked[0]
def peek(self):
"""Read one line without changing the file cursor."""
if self.peeked is None:
pos = self.fp.tell()
tmp = self.fp.readline()
if len(tmp) == 0: return None
self.peeked = (pos, tmp)
return self.peeked[1]
def read(self):
"""Read one line from the file, and move the file cursor to the next line."""
if self.peeked is None:
tmp = self.fp.readline()
if len(tmp) == 0: return None
return tmp
tmp, self.peeked = self.peeked, None
return tmp[1]
def read_patch(filename):
"""Iterates over all patches contained in a file, and returns PatchObject objects."""
class _FileReader(object):
def __init__(self, filename):
self.filename = filename
self.fp = open(self.filename)
self.peeked = None
def close(self):
self.fp.close()
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.close()
def seek(self, pos):
"""Change the file cursor position."""
self.fp.seek(pos)
self.peeked = None
def tell(self):
"""Return the current file cursor position."""
if self.peeked is None:
return self.fp.tell()
return self.peeked[0]
def peek(self):
"""Read one line without changing the file cursor."""
if self.peeked is None:
pos = self.fp.tell()
tmp = self.fp.readline()
if len(tmp) == 0: return None
self.peeked = (pos, tmp)
return self.peeked[1]
def read(self):
"""Read one line from the file, and move the file cursor to the next line."""
if self.peeked is None:
tmp = self.fp.readline()
if len(tmp) == 0: return None
return tmp
tmp, self.peeked = self.peeked, None
return tmp[1]
def _read_single_patch(fp, header, oldname=None, newname=None):
"""Internal function to read a single patch from a file."""
@ -351,35 +336,29 @@ def read_patch(filename):
else:
assert fp.read() == line
def apply_patch(content, patches, reverse=False, fuzz=2):
def apply_patch(original, patchfile, reverse=False, fuzz=2):
"""Apply a patch with optional fuzz - uses the commandline 'patch' utility."""
if not isinstance(patches, collections.Sequence):
patches = [patches]
contentfile = tempfile.NamedTemporaryFile(delete=False)
result = tempfile.NamedTemporaryFile(delete=False)
try:
contentfile.write(content)
contentfile.close()
# We open the file again to avoid race-conditions with multithreaded reads
with open(original.name) as fp:
shutil.copyfileobj(fp, result)
result.close()
for patch in patches:
cmdline = ["patch", "--no-backup-if-mismatch", "--force", "--silent", "-r", "-"]
if reverse: cmdline.append("--reverse")
if fuzz != 2: cmdline.append("--fuzz=%d" % fuzz)
cmdline += [result.name, patchfile.name]
patchfile = patch.extract()
cmdline = ["patch", "--no-backup-if-mismatch", "--force", "--silent", "-r", "-"]
if reverse: cmdline.append("--reverse")
if fuzz != 2: cmdline.append("--fuzz=%d" % fuzz)
cmdline += [contentfile.name, patchfile.name]
with open(os.devnull, 'w') as devnull:
exitcode = subprocess.call(cmdline, stdout=devnull, stderr=devnull)
if exitcode != 0:
raise PatchApplyError("Failed to apply patch (exitcode %d)." % exitcode)
with open(contentfile.name) as fp:
content = fp.read()
finally:
os.unlink(contentfile.name)
return content
exitcode = subprocess.call(cmdline, stdout=_devnull, stderr=_devnull)
if exitcode != 0:
raise PatchApplyError("Failed to apply patch (exitcode %d)." % exitcode)
# Hack - we can't keep the file open while patching ('patch' might rename/replace
# the file), so create a new _TemporaryFileWrapper object for the existing path.
return tempfile._TemporaryFileWrapper(file=open(result.name, 'r+b'), \
name=result.name, delete=True)
except:
os.unlink(result.name)
raise

106
debian/tools/progressbar.py vendored Normal file
View File

@ -0,0 +1,106 @@
#!/usr/bin/python2
#
# Python progressbar functions.
#
# Copyright (C) 2014 Sebastian Lackner
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
import fcntl
import os
import signal
import struct
import sys
import termios
def _sig_winch(signum=None, frame=None):
"""Signal handler for SIGWINCH."""
global _term_width
h, w, hp, wp = struct.unpack('HHHH', fcntl.ioctl(sys.stdout.fileno(),
termios.TIOCGWINSZ, struct.pack('HHHH', 0, 0, 0, 0)))
_term_width = w
try:
_sig_winch()
signal.signal(signal.SIGWINCH, _sig_winch)
except IOError:
_term_width = int(os.environ.get('COLUMNS', 80)) - 1
class ProgressBar(object):
def __init__(self, desc="", msg=None, current=0, total=100):
"""Initialize a new progress bar with given properties."""
self.desc = desc
self.msg = msg
self.current = current
self.total = total
def __enter__(self):
self.update()
return self
def __exit__(self, type, value, traceback):
if type is not None:
sys.stdout.write("\r")
msg = "<interrupted>"
else:
msg = None
self.finish(msg)
if self.msg is not None:
sys.stdout.write("\n")
def update(self, value = None):
"""Redraw the progressbar and optionally update the value."""
if value is not None:
self.current = value
if self.current == 0 or (self.current >= self.total and self.msg is None):
sys.stdout.write("%s\r" % (' ' * _term_width))
sys.stdout.flush()
return
width = _term_width / 2
s1 = self.desc.ljust(width - 1, ' ')[:width - 1]
width = _term_width - width
if self.current >= self.total:
s2 = self.msg.ljust(width, ' ')[:width]
elif width > 2:
numbars = min(self.current * (width - 2) / self.total, width - 2)
s2 = "[%s%s]" % ('#' * numbars, '-' * (width - 2 - numbars))
percent = " %d%% " % min(self.current * 100 / self.total, 100)
i = (len(s2)-len(percent))/2
s2 = "%s%s%s" % (s2[:i], percent, s2[i+len(percent):])
sys.stdout.write("%s %s\r" % (s1, s2))
sys.stdout.flush()
def finish(self, msg = None):
"""Finalize the progressbar."""
if msg is not None:
self.msg = msg
self.current = self.total
self.update()
sys.stdout.flush()
if __name__ == '__main__':
import time
print ""
with ProgressBar(desc="description") as x:
for i in xrange(100):
x.update(i)
time.sleep(1)

View File

@ -27,7 +27,6 @@ PATCHLIST := \
atl-IOCS_Property.ok \
comctl32-LoadIconMetric.ok \
configure-Absolute_RPATH.ok \
configure-Detect_Gnutls.ok \
d3d9-Surface_Refcount.ok \
d3dx9_36-DXTn.ok \
d3dx9_36-Filter_Warnings.ok \
@ -109,7 +108,6 @@ PATCHLIST := \
wineboot-HKEY_DYN_DATA.ok \
winebuild-LinkerVersion.ok \
wined3d-DXTn.ok \
wined3d-FFP_Fog.ok \
wined3d-Revert_PixelFormat.ok \
winedevice-Fix_Relocation.ok \
winemenubuilder-Desktop_Icon_Path.ok \
@ -332,18 +330,6 @@ configure-Absolute_RPATH.ok:
echo '+ { "Sebastian Lackner", "configure: Also add the absolute RPATH when linking against libwine.", 1 },'; \
) > configure-Absolute_RPATH.ok
# Patchset configure-Detect_Gnutls
# |
# | Modified files:
# | * configure.ac
# |
.INTERMEDIATE: configure-Detect_Gnutls.ok
configure-Detect_Gnutls.ok:
$(call APPLY_FILE,configure-Detect_Gnutls/0001-configure-Fix-detection-of-gnutls-on-Ubuntu-14.10.patch)
@( \
echo '+ { "Sebastian Lackner", "configure: Fix detection of gnutls on Ubuntu 14.10.", 3 },'; \
) > configure-Detect_Gnutls.ok
# Patchset d3d9-Surface_Refcount
# |
# | This patchset fixes the following Wine bugs:
@ -1693,21 +1679,6 @@ wined3d-DXTn.ok:
echo '+ { "Michael Müller", "wined3d: add DXT1 to B4G4R4A4, DXT1 to B5G5R5A1 and DXT3 to B4G4R4A4 conversion.", 1 },'; \
) > wined3d-DXTn.ok
# Patchset wined3d-FFP_Fog
# |
# | Modified files:
# | * dlls/d3d8/tests/visual.c, dlls/d3d9/tests/visual.c, dlls/ddraw/tests/ddraw7.c, dlls/wined3d/directx.c,
# | dlls/wined3d/glsl_shader.c
# |
.INTERMEDIATE: wined3d-FFP_Fog.ok
wined3d-FFP_Fog.ok:
$(call APPLY_FILE,wined3d-FFP_Fog/0001-wined3d-Set-D3DPMISCCAPS_FOGVERTEXCLAMPED-flag-in-ge.patch)
$(call APPLY_FILE,wined3d-FFP_Fog/0002-wined3d-Take-abs-of-vertex-z-coordinate-as-FFP-fog-c.patch)
@( \
echo '+ { "Joachim Priesner", "wined3d: Set D3DPMISCCAPS_FOGVERTEXCLAMPED flag in get_device_caps (resend).", 1 },'; \
echo '+ { "Joachim Priesner", "wined3d: Take abs() of vertex z coordinate as FFP fog coordinate.", 7 },'; \
) > wined3d-FFP_Fog.ok
# Patchset wined3d-Revert_PixelFormat
# |
# | This patchset fixes the following Wine bugs:

View File

@ -1,25 +0,0 @@
From 3ec0224addff16f803d8d49feb0a4a232c48ebcd Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sun, 7 Sep 2014 08:09:54 +0200
Subject: configure: Fix detection of gnutls on Ubuntu 14.10. (try 3)
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
index 2bc0008..c22b119 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1360,7 +1360,7 @@ then
[AC_CHECK_HEADER(gnutls/gnutls.h,
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>]], [[static typeof(gnutls_mac_get_key_size) *func; if (func) return 0;]])],
- [WINE_CHECK_SONAME(gnutls,gnutls_global_init,,[GNUTLS_CFLAGS=""],[$GNUTLS_LIBS])])],
+ [WINE_CHECK_SONAME(gnutls,gnutls_global_init,,[GNUTLS_CFLAGS=""],[$GNUTLS_LIBS],[[libgnutls\\(-deb0\\)\\{0,1\\}]])])],
[GNUTLS_CFLAGS=""])])
fi
WINE_WARNING_WITH(gnutls,[test "x$ac_cv_lib_soname_gnutls" = "x"],
--
2.1.0

View File

@ -1,34 +0,0 @@
From 77f3334105cbefa51452f8d7ad68c7ba5b797837 Mon Sep 17 00:00:00 2001
From: Joachim Priesner <joachim.priesner@web.de>
Date: Fri, 14 Nov 2014 22:39:58 +0100
Subject: wined3d: Set D3DPMISCCAPS_FOGVERTEXCLAMPED flag in get_device_caps
(resend)
Wine clamps the oFog output of vertex shaders. Tests for the flag follow
in the second part of this patch.
---
dlls/wined3d/directx.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index 794496b..d31202a 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -4165,12 +4165,12 @@ HRESULT CDECL wined3d_get_device_caps(const struct wined3d *wined3d, UINT adapte
WINED3DPMISCCAPS_CLIPPLANESCALEDPOINTS |
WINED3DPMISCCAPS_MASKZ |
WINED3DPMISCCAPS_BLENDOP |
+ WINED3DPMISCCAPS_FOGVERTEXCLAMPED |
WINED3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING;
/* TODO:
WINED3DPMISCCAPS_NULLREFERENCE
WINED3DPMISCCAPS_FOGANDSPECULARALPHA
- WINED3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS
- WINED3DPMISCCAPS_FOGVERTEXCLAMPED */
+ WINED3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS */
if (gl_info->supported[EXT_BLEND_EQUATION_SEPARATE] && gl_info->supported[EXT_BLEND_FUNC_SEPARATE])
caps->PrimitiveMiscCaps |= WINED3DPMISCCAPS_SEPARATEALPHABLEND;
--
2.1.3

View File

@ -1,996 +0,0 @@
From 5180456ab36112ce0f12b0fd9eddc29b860430be Mon Sep 17 00:00:00 2001
From: Joachim Priesner <joachim.priesner@web.de>
Date: Fri, 14 Nov 2014 22:40:11 +0100
Subject: wined3d: Take abs() of vertex z coordinate as FFP fog coordinate (try
7)
Take the absolute value of vertex.z as fog coordinate instead of just
the z coordinate. This fixes e.g. fog for applications that use
right-handed projection matrices.
Also test the clamp behavior of the oFog vertex shader output.
Tested on openSuse 13.1 and Windows 8.1 (VMware Player).
Try 7: Also test fogstart=-1, fogend=0.
---
dlls/d3d8/tests/visual.c | 331 +++++++++++++++++++++++++++++++++++++
dlls/d3d9/tests/visual.c | 399 +++++++++++++++++++++++++++++++++++++++++++++
dlls/ddraw/tests/ddraw7.c | 174 ++++++++++++++++++++
dlls/wined3d/glsl_shader.c | 2 +-
4 files changed, 905 insertions(+), 1 deletion(-)
diff --git a/dlls/d3d8/tests/visual.c b/dlls/d3d8/tests/visual.c
index cbede9d..af4a16d 100644
--- a/dlls/d3d8/tests/visual.c
+++ b/dlls/d3d8/tests/visual.c
@@ -696,6 +696,336 @@ done:
DestroyWindow(window);
}
+/* This test tests fog in combination with negative vertex z coordinates. */
+static void fog_negative_z_test(void)
+{
+ enum
+ {
+ C_ALPHA_0x00 = 0x00000000,
+ C_CLEAR = 0xffff00ff,
+ C_FOGGED = 0x0000ff00,
+ C_HALF_FOGGED = 0x00808000,
+ C_UNFOGGED = 0x00ff0000,
+ C_CLAMPED_FOG = 0xdeadbeef /* triggers special codepath when used as middle_color */
+ };
+
+ /* Fill the null-shader entry with the FVF (SetVertexShader is "overloaded" on d3d8). */
+ DWORD vertex_shader[3] = {D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, 0, 0};
+ DWORD pixel_shader[2] = {0, 0};
+ IDirect3D8 *d3d;
+ IDirect3DDevice8 *device;
+ BOOL has_vs_support, has_ps_support, has_table_fog_support;
+ unsigned int i, ps, y;
+ D3DCOLOR color, expected_middle_color;
+ ULONG refcount;
+ D3DCAPS8 caps;
+ HWND window;
+ HRESULT hr;
+
+ static const DWORD vertex_decl[] =
+ {
+ D3DVSD_STREAM(0),
+ D3DVSD_REG(0, D3DVSDT_FLOAT3), /* position, v0 */
+ D3DVSD_REG(1, D3DVSDT_D3DCOLOR), /* diffuse color, v1 */
+ D3DVSD_REG(2, D3DVSDT_D3DCOLOR), /* specular color, v2 */
+ D3DVSD_CONST(0, 1), 0x3f000000, 0x00000000, 0x00000000, 0x00000000, /* def c0, 0.5, 0, 0, 0 */
+ D3DVSD_END()
+ };
+
+ /* Basic vertex shader without fog computation ("non foggy") */
+ static const DWORD vertex_shader_code1[] =
+ {
+ 0xfffe0100, /* vs_1_0 */
+ /* output.Pos.z = input.Pos.z * 0.5 + 0.5 */
+ 0x00000005, 0x80010000, 0x90aa0000, 0xa0000000, /* mul r0.x, v0.z, c0.x */
+ 0x00000002, 0xc0040000, 0x80000000, 0xa0000000, /* add oPos.z, r0.x c0.x */
+ /* output.Pos.xyw = input.Pos.xyw */
+ 0x00000001, 0xc00b0000, 0x90e40000, /* mov oPos.xyw, v0 */
+ /* output.Color = input.Color */
+ 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */
+ 0x0000ffff, /* END */
+ };
+
+ /* Basic vertex shader with fog computation ("foggy"). Outputs the z coordinate of the vertex
+ * as its fog intensity value. */
+ static const DWORD vertex_shader_code2[] =
+ {
+ 0xfffe0100, /* vs_1_0 */
+ /* output.Pos.z = input.Pos.z * 0.5 + 0.5 */
+ 0x00000005, 0x80010000, 0x90aa0000, 0xa0000000, /* mul r0.x, v0.z, c0.x */
+ 0x00000002, 0xc0040000, 0x80000000, 0xa0000000, /* add oPos.z, r0.x c0.x */
+ /* output.Pos.xyw = input.Pos.xyw */
+ 0x00000001, 0xc00b0000, 0x90e40000, /* mov oPos.xyw, v0 */
+ /* output.Color = input.Color */
+ 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */
+ /* output.Fog = input.Pos.z */
+ 0x00000001, 0xc00f0001, 0x90aa0000, /* mov oFog, v0.z */
+ 0x0000ffff, /* END */
+ };
+
+ /* Basic pixel shader */
+ static const DWORD pixel_shader_code[] =
+ {
+ 0xffff0101, /* ps_1_1 */
+ 0x00000001, 0x800f0000, 0x90e40000, /* mov r0, v0 */
+ 0x0000ffff
+ };
+
+ static const struct
+ {
+ struct vec3 position;
+ DWORD diffuse;
+ DWORD specular;
+ }
+ top_quad[] =
+ {
+ {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, -1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ },
+ bottom_quad_1[] =
+ {
+ {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{-1.0f, 1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ },
+ bottom_quad_2[] =
+ {
+ {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ };
+ static const struct
+ {
+ int vshader;
+ float fog_start;
+ float fog_end;
+ D3DFOGMODE vfog;
+ D3DFOGMODE tfog;
+ DWORD color_left;
+ DWORD color_middle_top;
+ DWORD color_middle_bottom;
+ DWORD color_right;
+ }
+ test_data[] =
+ {
+ /* fogstart = 0, fogend = 1:
+ * Using z-based fog, the bottom quad should have a gradient UNFOGGED->FOGGED
+ * for table fog, and be completely fogged for vertex fog.
+ * When the fog coordinate returned by the vertex shader is used instead,
+ * the result is a gradient FOGGED->UNFOGGED.
+ *
+ * fogstart = -1, fogend = 0:
+ * Both quads are completely fogged, except in the cases where the foggy vertex shader
+ * with vertex fog or neither vertex nor table fog are used. Those are independent of
+ * fogstart/fogend, the result should therefore equal that of the
+ * corresponding (fogstart, fogend) == (0, 1) case. */
+
+ /* No vertex shader */
+ {0, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {0, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {0, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_UNFOGGED, C_FOGGED, C_FOGGED},
+ {0, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ {0, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {0, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {0, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {0, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ /* Vertex shader without vertex fog computation */
+ {1, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {1, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {1, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {1, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ {1, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {1, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {1, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {1, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ /* Vertex shader with vertex fog computation.
+ * As drivers inconsistently clamp or don't clamp the oFog output of vertex shaders,
+ * the special value C_CLAMPED_FOG allows both C_HALF_FOGGED (clamped) and C_FOGGED. */
+ {2, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {2, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {2, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+ {2, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+
+ {2, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {2, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {2, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+ {2, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+ };
+ /* Affine projection matrix for z-near = -1, z-far = 1.
+ * Note that for the tests with vertex shaders, this matrix is hard-coded in the shaders. */
+ static const D3DMATRIX proj_mat =
+ {{{
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 1.0f
+ }}};
+
+ window = CreateWindowA("static", "d3d8_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ 0, 0, 640, 480, NULL, NULL, NULL, NULL);
+ d3d = Direct3DCreate8(D3D_SDK_VERSION);
+ ok(!!d3d, "Failed to create a D3D object\n");
+ if (!(device = create_device(d3d, window, window, TRUE)))
+ {
+ skip("Failed to create a D3D device, skipping tests\n");
+ goto done;
+ }
+
+ hr = IDirect3DDevice8_GetDeviceCaps(device, &caps);
+ ok(SUCCEEDED(hr), "Failed to get device caps (%#x)\n", hr);
+
+ has_table_fog_support = caps.RasterCaps & D3DPRASTERCAPS_FOGTABLE;
+ if (!has_table_fog_support)
+ skip("No table fog support, skipping some fog tests\n");
+
+ has_ps_support = caps.PixelShaderVersion >= D3DPS_VERSION(1, 1);
+ if (has_ps_support)
+ {
+ hr = IDirect3DDevice8_CreatePixelShader(device, pixel_shader_code, &pixel_shader[1]);
+ ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr);
+ }
+ else
+ {
+ skip("No ps_1_1 support, skipping some fog tests\n");
+ }
+
+ has_vs_support = caps.VertexShaderVersion >= D3DVS_VERSION(1, 0);
+ if (has_vs_support)
+ {
+ hr = IDirect3DDevice8_CreateVertexShader(device, vertex_decl, vertex_shader_code1, &vertex_shader[1], 0);
+ ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_CreateVertexShader(device, vertex_decl, vertex_shader_code2, &vertex_shader[2], 0);
+ ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+ }
+ else
+ {
+ skip("No vs_1_0 support, skipping some fog tests\n");
+ }
+
+ /* Setup initial states: No depth test, no lighting, fog on, fog color */
+ hr = IDirect3DDevice8_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE);
+ ok(SUCCEEDED(hr), "Turning off depth test failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_SetRenderState(device, D3DRS_LIGHTING, FALSE);
+ ok(SUCCEEDED(hr), "Turning off lighting failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGENABLE, TRUE);
+ ok(SUCCEEDED(hr), "Turning on fog calculations failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGCOLOR, C_FOGGED);
+ ok(SUCCEEDED(hr), "Setting fog color failed (%#x)\n", hr);
+
+ hr = IDirect3DDevice8_SetTransform(device, D3DTS_PROJECTION, &proj_mat);
+ ok(SUCCEEDED(hr), "Failed to set projection transform (%#x)\n", hr);
+
+ for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++)
+ {
+ if (test_data[i].vshader != 0 && !vertex_shader[test_data[i].vshader])
+ continue;
+ if (test_data[i].tfog != D3DFOG_NONE && !has_table_fog_support)
+ continue;
+
+ hr = IDirect3DDevice8_SetVertexShader(device, vertex_shader[test_data[i].vshader]);
+ ok(SUCCEEDED(hr), "SetVertexShader failed (%#x)\n", hr);
+
+ for (ps = 0; ps < sizeof(pixel_shader)/sizeof(pixel_shader[0]); ps++)
+ {
+ if (ps != 0 && !pixel_shader[ps])
+ continue;
+
+ if (has_ps_support)
+ {
+ hr = IDirect3DDevice8_SetPixelShader(device, pixel_shader[ps]);
+ ok(SUCCEEDED(hr), "SetPixelShader failed (%#x)\n", hr);
+ }
+
+ hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGVERTEXMODE, test_data[i].vfog);
+ ok(SUCCEEDED(hr), "Setting fog vertex mode to %d failed (%#x)\n", test_data[i].vfog, hr);
+ hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGTABLEMODE, test_data[i].tfog);
+ ok(SUCCEEDED(hr), "Setting fog table mode to %d failed (%#x)\n", test_data[i].tfog, hr);
+
+ hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGSTART,
+ *(DWORD*)(&test_data[i].fog_start));
+ ok(SUCCEEDED(hr), "Setting fog start to %.1f failed (%#x)\n", test_data[i].fog_start, hr);
+ hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGEND,
+ *(DWORD*)(&test_data[i].fog_end));
+ ok(SUCCEEDED(hr), "Setting fog end to %.1f failed (%#x)\n", test_data[i].fog_end, hr);
+
+ hr = IDirect3DDevice8_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0);
+ ok(SUCCEEDED(hr), "Clear failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_BeginScene(device);
+ ok(SUCCEEDED(hr), "BeginScene failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_DrawPrimitiveUP(
+ device, D3DPT_TRIANGLESTRIP, 2, &top_quad[0], sizeof(top_quad[0]));
+ ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_DrawPrimitiveUP(
+ device, D3DPT_TRIANGLESTRIP, 2, &bottom_quad_1[0], sizeof(bottom_quad_1[0]));
+ ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_DrawPrimitiveUP(
+ device, D3DPT_TRIANGLESTRIP, 2, &bottom_quad_2[0], sizeof(bottom_quad_2[0]));
+ ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+ hr = IDirect3DDevice8_EndScene(device);
+ ok(SUCCEEDED(hr), "EndScene failed (%#x)\n", hr);
+
+ /* Use 5% tolerance on the colors since there may be a gradient
+ * between left and right vertices. */
+ for (y = 120; y <= 360; y += 240)
+ {
+ color = getPixelColor(device, 2, y);
+ ok(color_match(color, test_data[i].color_left, 13),
+ "fog %.1f-%.1f vs%i ps%i fvm%i ftm%i, y=%i: "
+ "got left color %08x, expected %08x+-5%%\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vshader, ps,
+ test_data[i].vfog, test_data[i].tfog, y, color, test_data[i].color_left);
+ color = getPixelColor(device, 320, y);
+ expected_middle_color =
+ y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom;
+ if (expected_middle_color == C_CLAMPED_FOG)
+ {
+ ok(color_match(color, C_HALF_FOGGED, 13) || color_match(color, C_FOGGED, 13),
+ "fog %.1f-%.1f vs%i ps%i fvm%i ftm%i, y=%i: "
+ "got middle color %08x, expected %08x+-5%% or %08x+-5%%\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vshader, ps,
+ test_data[i].vfog, test_data[i].tfog, y, color, C_HALF_FOGGED, C_FOGGED);
+ }
+ else
+ {
+ ok(color_match(color, expected_middle_color, 13),
+ "fog %.1f-%.1f vs%i ps%i fvm%i ftm%i, y=%i: "
+ "got middle color %08x, expected %08x+-5%%\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vshader, ps,
+ test_data[i].vfog, test_data[i].tfog, y, color, expected_middle_color);
+ }
+ color = getPixelColor(device, 638, y);
+ ok(color_match(color, test_data[i].color_right, 13),
+ "fog %.1f-%.1f vs%i ps%i fvm%i ftm%i, y=%i: "
+ "got right color %08x, expected %08x+-5%%\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vshader, ps,
+ test_data[i].vfog, test_data[i].tfog, y, color, test_data[i].color_right);
+ }
+
+ hr = IDirect3DDevice8_Present(device, NULL, NULL, NULL, NULL);
+ ok(SUCCEEDED(hr), "Present failed (%#x)\n", hr);
+ }
+ }
+ for (i = 0; i < sizeof(vertex_shader)/sizeof(vertex_shader[0]); i++)
+ if (vertex_shader[i])
+ IDirect3DDevice8_DeleteVertexShader(device, vertex_shader[i]);
+ for (i = 0; i < sizeof(pixel_shader)/sizeof(pixel_shader[0]); i++)
+ if (pixel_shader[i])
+ IDirect3DDevice8_DeletePixelShader(device, pixel_shader[i]);
+ refcount = IDirect3DDevice8_Release(device);
+ ok(!refcount, "Device has %u references left\n", refcount);
+done:
+ IDirect3D8_Release(d3d);
+ DestroyWindow(window);
+}
+
/* This tests fog in combination with shaders.
* What's tested: linear fog (vertex and table) with pixel shader
* linear table fog with non foggy vertex shader
@@ -5395,6 +5725,7 @@ START_TEST(visual)
offscreen_test();
test_blend();
test_scalar_instructions();
+ fog_negative_z_test();
fog_with_shader_test();
cnd_test();
p8_texture_test();
diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c
index fbab386..978e99d 100644
--- a/dlls/d3d9/tests/visual.c
+++ b/dlls/d3d9/tests/visual.c
@@ -1825,6 +1825,404 @@ done:
DestroyWindow(window);
}
+/* This test tests fog in combination with negative vertex z coordinates. */
+static void fog_negative_z_test(void)
+{
+ enum
+ {
+ C_ALPHA_0x00 = 0x00000000,
+ C_CLEAR = 0xffff00ff,
+ C_FOGGED = 0x0000ff00,
+ C_HALF_FOGGED = 0x00808000,
+ C_UNFOGGED = 0x00ff0000,
+ C_CLAMPED_FOG = 0xdeadbeef /* triggers special codepath when used as middle_color */
+ };
+
+ IDirect3DVertexShader9 *vertex_shader[4] = {NULL, NULL, NULL, NULL};
+ IDirect3DPixelShader9 *pixel_shader[3] = {NULL, NULL, NULL};
+ IDirect3DVertexDeclaration9 *vertex_declaration = NULL;
+ IDirect3DDevice9 *device;
+ BOOL has_vs_support, has_ps_support, has_table_fog_support, is_fog_vertex_clamped;
+ unsigned int i, ps, y;
+ IDirect3D9 *d3d;
+ ULONG refcount;
+ D3DCAPS9 caps;
+ DWORD color, expected_middle_color;
+ HWND window;
+ HRESULT hr;
+
+ /* Basic vertex shader without fog computation ("non foggy") */
+ static const DWORD vertex_shader_code1[] =
+ {
+ 0xfffe0101, /* vs_1_1 */
+ 0x00000051, 0xa00f0000, 0x3f000000, 0x00000000, 0x00000000, 0x00000000, /* def c0, 0.5, 0, 0, 0 */
+ 0x0000001f, 0x80000000, 0x900f0000, /* dcl_position0 v0 */
+ 0x0000001f, 0x8000000a, 0x900f0001, /* dcl_color0 v1 */
+ /* output.Pos.z = input.Pos.z * 0.5 + 0.5 */
+ 0x00000005, 0x80010000, 0x90aa0000, 0xa0000000, /* mul r0.x, v0.z, c0.x */
+ 0x00000002, 0xc0040000, 0x80000000, 0xa0000000, /* add oPos.z, r0.x c0.x */
+ /* output.Pos.xyw = input.Pos.xyw */
+ 0x00000001, 0xc00b0000, 0x90e40000, /* mov oPos.xyw, v0 */
+ /* output.Color = input.Color */
+ 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */
+ 0x0000ffff, /* END */
+ };
+
+ /* Basic vertex shader with fog computation ("foggy"). Outputs the z coordinate of the vertex
+ * as its fog intensity value. */
+ static const DWORD vertex_shader_code2[] =
+ {
+ 0xfffe0101, /* vs_1_1 */
+ 0x00000051, 0xa00f0000, 0x3f000000, 0x00000000, 0x00000000, 0x00000000, /* def c0, 0.5, 0, 0, 0 */
+ 0x0000001f, 0x80000000, 0x900f0000, /* dcl_position0 v0 */
+ 0x0000001f, 0x8000000a, 0x900f0001, /* dcl_color0 v1 */
+ /* output.Pos.z = input.Pos.z * 0.5 + 0.5 */
+ 0x00000005, 0x80010000, 0x90aa0000, 0xa0000000, /* mul r0.x, v0.z, c0.x */
+ 0x00000002, 0xc0040000, 0x80000000, 0xa0000000, /* add oPos.z, r0.x c0.x */
+ /* output.Pos.xyw = input.Pos.xyw */
+ 0x00000001, 0xc00b0000, 0x90e40000, /* mov oPos.xyw, v0 */
+ /* output.Color = input.Color */
+ 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */
+ /* output.Fog = input.Pos.z */
+ 0x00000001, 0xc00f0001, 0x90aa0000, /* mov oFog, v0.z */
+ 0x0000ffff, /* END */
+ };
+
+ /* Basic vertex shader with fog computation ("foggy"), vs_2_0 */
+ static const DWORD vertex_shader_code3[] =
+ {
+ 0xfffe0200, /* vs_2_0 */
+ 0x05000051, 0xa00f0000, 0x3f000000, 0x00000000, 0x00000000, 0x00000000, /* def c0, 0.5, 0, 0, 0 */
+ 0x0200001f, 0x80000000, 0x900f0000, /* dcl_position0 v0 */
+ 0x0200001f, 0x8000000a, 0x900f0001, /* dcl_color0 v1 */
+ /* output.Pos.z = input.Pos.z * 0.5 + 0.5 */
+ 0x03000005, 0x80010000, 0x90aa0000, 0xa0000000, /* mul r0.x, v0.z, c0.x */
+ 0x03000002, 0xc0040000, 0x80000000, 0xa0000000, /* add oPos.z, r0.x c0.x */
+ /* output.Pos.xyw = input.Pos.xyw */
+ 0x02000001, 0xc00b0000, 0x90e40000, /* mov oPos.xyw, v0 */
+ /* output.Color = input.Color */
+ 0x02000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */
+ /* output.Fog = input.Pos.z */
+ 0x02000001, 0xc00f0001, 0x90aa0000, /* mov oFog, v0.z */
+ 0x0000ffff, /* END */
+ };
+
+ /* Basic pixel shader */
+ static const DWORD pixel_shader_code[] =
+ {
+ 0xffff0101, /* ps_1_1 */
+ 0x00000001, 0x800f0000, 0x90e40000, /* mov r0, v0 */
+ 0x0000ffff
+ };
+ static const DWORD pixel_shader_code2[] =
+ {
+ 0xffff0200, /* ps_2_0 */
+ 0x0200001f, 0x80000000, 0x900f0000, /* dcl v0 */
+ 0x02000001, 0x800f0800, 0x90e40000, /* mov oC0, v0 */
+ 0x0000ffff
+ };
+ static const struct
+ {
+ struct vec3 position;
+ D3DCOLOR diffuse;
+ D3DCOLOR specular;
+ }
+ top_quad[] =
+ {
+ {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, -1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ },
+ bottom_quad_1[] =
+ {
+ {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{-1.0f, 1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ },
+ bottom_quad_2[] =
+ {
+ {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ };
+
+ static const D3DVERTEXELEMENT9 decl_elements[] =
+ {
+ {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+ {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, /* diffuse */
+ {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1}, /* specular */
+ D3DDECL_END()
+ };
+ static const struct
+ {
+ int vshader;
+ float fog_start;
+ float fog_end;
+ D3DFOGMODE vfog;
+ D3DFOGMODE tfog;
+ DWORD color_left;
+ DWORD color_middle_top;
+ DWORD color_middle_bottom;
+ DWORD color_right;
+ }
+ test_data[] =
+ {
+ /* fogstart = 0, fogend = 1:
+ * Using z-based fog, the bottom quad should have a gradient UNFOGGED->FOGGED
+ * for table fog, and be completely fogged for vertex fog.
+ * When the fog coordinate returned by the vertex shader is used instead,
+ * the result is a gradient FOGGED->UNFOGGED in the right half of the screen.
+ *
+ * fogstart = -1, fogend = 0:
+ * Both quads are completely fogged, except in the cases where a foggy vertex shader
+ * with vertex fog or neither vertex nor table fog are used. Those are independent of
+ * fogstart/fogend, the result should therefore equal that of the
+ * corresponding (fogstart, fogend) == (0, 1) case.
+ *
+ * C_CLAMPED_FOG will be replaced by the correct expected value based on
+ * the value of D3DPMISCCAPS_FOGVERTEXCLAMPED. If the driver clamps the oFog
+ * output of vertex shaders, it should be C_HALF_FOGGED, else C_FOGGED. */
+
+ /* No vertex shader */
+ {0, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {0, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {0, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_UNFOGGED, C_FOGGED, C_FOGGED},
+ {0, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ {0, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {0, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {0, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {0, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ /* Vertex shader without vertex fog computation */
+ {1, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {1, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {1, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {1, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ {1, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {1, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {1, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {1, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ /* Vertex shader vs_1_1 with vertex fog computation */
+ {2, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {2, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {2, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+ {2, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+
+ {2, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {2, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {2, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+ {2, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+
+ /* Vertex shader vs_2_0 with vertex fog computation */
+ {3, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {3, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ {3, 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+ {3, 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+
+ {3, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {3, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {3, -1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+ {3, -1.0f, 0.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_CLAMPED_FOG, C_UNFOGGED},
+ };
+ /* Affine projection matrix for z-near = -1, z-far = 1.
+ * Note that for the tests with vertex shaders, this matrix is hard-coded in the shaders. */
+ static const D3DMATRIX proj_mat =
+ {{{
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 1.0f,
+ }}};
+
+ window = CreateWindowA("static", "d3d9_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ 0, 0, 640, 480, NULL, NULL, NULL, NULL);
+ d3d = Direct3DCreate9(D3D_SDK_VERSION);
+ ok(!!d3d, "Failed to create a D3D object\n");
+ if (!(device = create_device(d3d, window, window, TRUE)))
+ {
+ skip("Failed to create a D3D device, skipping tests\n");
+ goto done;
+ }
+
+ hr = IDirect3DDevice9_GetDeviceCaps(device, &caps);
+ ok(SUCCEEDED(hr), "Failed to get device caps (%#x)\n", hr);
+
+ has_table_fog_support = caps.RasterCaps & D3DPRASTERCAPS_FOGTABLE;
+ if (!has_table_fog_support)
+ skip("No table fog support, skipping some fog tests\n");
+
+ has_vs_support = caps.VertexShaderVersion >= D3DVS_VERSION(1, 1);
+ if (has_vs_support)
+ {
+ hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code1, &vertex_shader[1]);
+ ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code2, &vertex_shader[2]);
+ ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+
+ if (caps.VertexShaderVersion >= D3DVS_VERSION(2, 0))
+ {
+ hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code3, &vertex_shader[3]);
+ ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr);
+ }
+ else
+ {
+ skip("No vs_2_0 support, skipping some fog tests\n");
+ }
+ }
+ else
+ {
+ skip("No vs_1_1 support, skipping some fog tests\n");
+ }
+
+ has_ps_support = caps.PixelShaderVersion >= D3DPS_VERSION(1, 1);
+ if (has_ps_support)
+ {
+ hr = IDirect3DDevice9_CreatePixelShader(device, pixel_shader_code, &pixel_shader[1]);
+ ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr);
+
+ if (caps.PixelShaderVersion >= D3DPS_VERSION(2, 0))
+ {
+ hr = IDirect3DDevice9_CreatePixelShader(device, pixel_shader_code2, &pixel_shader[2]);
+ ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr);
+ }
+ else
+ {
+ skip("No ps_2_0 support, skipping some fog tests\n");
+ }
+ }
+ else
+ {
+ skip("No ps_1_1 support, skipping some fog tests\n");
+ }
+
+ is_fog_vertex_clamped = caps.PrimitiveMiscCaps & D3DPMISCCAPS_FOGVERTEXCLAMPED;
+
+ hr = IDirect3DDevice9_CreateVertexDeclaration(device, decl_elements, &vertex_declaration);
+ ok(SUCCEEDED(hr), "CreateVertexDeclaration failed (%#x)\n", hr);
+
+ /* Setup initial states: No depth test, no lighting, fog on, fog color */
+ hr = IDirect3DDevice9_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE);
+ ok(SUCCEEDED(hr), "Turning off depth test failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE);
+ ok(SUCCEEDED(hr), "Turning off lighting failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGENABLE, TRUE);
+ ok(SUCCEEDED(hr), "Turning on fog calculations failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGCOLOR, C_FOGGED);
+ ok(SUCCEEDED(hr), "Setting fog color failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_SetVertexDeclaration(device, vertex_declaration);
+ ok(SUCCEEDED(hr), "SetVertexDeclaration failed (%#x)\n", hr);
+
+ hr = IDirect3DDevice9_SetTransform(device, D3DTS_PROJECTION, &proj_mat);
+ ok(SUCCEEDED(hr), "Failed to set projection transform (%#x)\n", hr);
+
+ for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++)
+ {
+ if (test_data[i].vshader != 0 && !vertex_shader[test_data[i].vshader])
+ continue;
+ if (test_data[i].tfog != D3DFOG_NONE && !has_table_fog_support)
+ continue;
+
+ if (has_vs_support)
+ {
+ hr = IDirect3DDevice9_SetVertexShader(device, vertex_shader[test_data[i].vshader]);
+ ok(SUCCEEDED(hr), "SetVertexShader failed (%#x)\n", hr);
+ }
+
+ for (ps = 0; ps < sizeof(pixel_shader)/sizeof(pixel_shader[0]); ps++)
+ {
+ if (ps != 0 && !pixel_shader[ps])
+ continue;
+
+ if (has_ps_support)
+ {
+ hr = IDirect3DDevice9_SetPixelShader(device, pixel_shader[ps]);
+ ok(SUCCEEDED(hr), "SetPixelShader failed (%#x)\n", hr);
+ }
+
+ hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGVERTEXMODE, test_data[i].vfog);
+ ok(SUCCEEDED(hr), "Setting fog vertex mode to %d failed (%#x)\n", test_data[i].vfog, hr);
+ hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGTABLEMODE, test_data[i].tfog);
+ ok(SUCCEEDED(hr), "Setting fog table mode to %d failed (%#x)\n", test_data[i].tfog, hr);
+
+ hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGSTART,
+ *(DWORD*)(&test_data[i].fog_start));
+ ok(SUCCEEDED(hr), "Setting fog start to %.1f failed (%#x)\n", test_data[i].fog_start, hr);
+ hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGEND,
+ *(DWORD*)(&test_data[i].fog_end));
+ ok(SUCCEEDED(hr), "Setting fog end to %.1f failed (%#x)\n", test_data[i].fog_end, hr);
+
+ hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0);
+ ok(SUCCEEDED(hr), "Clear failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_BeginScene(device);
+ ok(SUCCEEDED(hr), "BeginScene failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_DrawPrimitiveUP(
+ device, D3DPT_TRIANGLESTRIP, 2, &top_quad[0], sizeof(top_quad[0]));
+ ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_DrawPrimitiveUP(
+ device, D3DPT_TRIANGLESTRIP, 2, &bottom_quad_1[0], sizeof(bottom_quad_1[0]));
+ ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_DrawPrimitiveUP(
+ device, D3DPT_TRIANGLESTRIP, 2, &bottom_quad_2[0], sizeof(bottom_quad_2[0]));
+ ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr);
+ hr = IDirect3DDevice9_EndScene(device);
+ ok(SUCCEEDED(hr), "EndScene failed (%#x)\n", hr);
+
+ /* Use 5% tolerance on the colors since there may be a gradient
+ * between left and right vertices. */
+ for (y = 120; y <= 360; y += 240)
+ {
+ color = getPixelColor(device, 2, y);
+ ok(color_match(color, test_data[i].color_left, 13),
+ "fog %.1f-%.1f vs%i ps%i fvm%i ftm%i, y=%i: "
+ "got left color %08x, expected %08x+-5%%\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vshader, ps,
+ test_data[i].vfog, test_data[i].tfog, y, color, test_data[i].color_left);
+ color = getPixelColor(device, 320, y);
+ expected_middle_color =
+ y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom;
+ if (expected_middle_color == C_CLAMPED_FOG)
+ {
+ expected_middle_color = is_fog_vertex_clamped ? C_HALF_FOGGED : C_FOGGED;
+ }
+ ok(color_match(color, expected_middle_color, 13),
+ "fog %.1f-%.1f vs%i ps%i fvm%i ftm%i, y=%i: "
+ "got middle color %08x, expected %08x+-5%%\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vshader, ps,
+ test_data[i].vfog, test_data[i].tfog, y, color, expected_middle_color);
+ color = getPixelColor(device, 638, y);
+ ok(color_match(color, test_data[i].color_right, 13),
+ "fog %.1f-%.1f vs%i ps%i fvm%i ftm%i, y=%i: "
+ "got right color %08x, expected %08x+-5%%\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vshader, ps,
+ test_data[i].vfog, test_data[i].tfog, y, color, test_data[i].color_right);
+
+ }
+
+ hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
+ ok(SUCCEEDED(hr), "Present failed (%#x)\n", hr);
+ }
+ }
+
+ for (i = 0; i < sizeof(vertex_shader)/sizeof(vertex_shader[0]); i++)
+ if (vertex_shader[i])
+ IDirect3DVertexShader9_Release(vertex_shader[i]);
+ for (i = 0; i < sizeof(pixel_shader)/sizeof(pixel_shader[0]); i++)
+ if (pixel_shader[i])
+ IDirect3DPixelShader9_Release(pixel_shader[i]);
+ IDirect3DVertexDeclaration9_Release(vertex_declaration);
+ refcount = IDirect3DDevice9_Release(device);
+ ok(!refcount, "Device has %u references left\n", refcount);
+done:
+ IDirect3D9_Release(d3d);
+ DestroyWindow(window);
+}
+
/* This test tests fog in combination with shaders.
* What's tested: linear fog (vertex and table) with pixel shader
* linear table fog with non foggy vertex shader
@@ -17033,6 +17431,7 @@ START_TEST(visual)
test_vshader_input();
test_vshader_float16();
stream_test();
+ fog_negative_z_test();
fog_with_shader_test();
texbem_test();
texdepth_test();
diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c
index 9263e1e..1d6585c 100644
--- a/dlls/ddraw/tests/ddraw7.c
+++ b/dlls/ddraw/tests/ddraw7.c
@@ -3636,6 +3636,179 @@ static void test_fog_special(void)
DestroyWindow(window);
}
+/* This test tests fog in combination with negative vertex z coordinates. */
+static void test_fog_negative_z(void)
+{
+ enum
+ {
+ C_ALPHA_0x00 = 0x00000000,
+ C_CLEAR = 0xffff00ff,
+ C_FOGGED = 0x0000ff00,
+ C_HALF_FOGGED = 0x00808000,
+ C_UNFOGGED = 0x00ff0000
+ };
+
+ D3DCOLOR color, expected_middle_color;
+ HRESULT hr;
+ ULONG refcount;
+ BOOL has_table_fog_support;
+ unsigned int i, y;
+ HWND window;
+ IDirect3DDevice7 *device;
+ IDirectDrawSurface7 *rt;
+ D3DDEVICEDESC7 caps;
+
+ static struct
+ {
+ struct vec3 position;
+ D3DCOLOR diffuse;
+ D3DCOLOR specular;
+ }
+ top_quad[] =
+ {
+ {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, -1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ },
+ bottom_quad_1[] =
+ {
+ {{-1.0f, 1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ },
+ bottom_quad_2[] =
+ {
+ {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00},
+ };
+ static const struct
+ {
+ float fog_start, fog_end;
+ DWORD vertexmode, tablemode;
+ D3DCOLOR color_left, color_middle_top, color_middle_bottom, color_right;
+ }
+ test_data[] =
+ {
+ { 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ { 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED},
+ { 0.0f, 1.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_UNFOGGED, C_FOGGED, C_FOGGED},
+ { 0.0f, 1.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+
+ {-1.0f, 0.0f, D3DFOG_NONE, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {-1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_LINEAR, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {-1.0f, 0.0f, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ {-1.0f, 0.0f, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED},
+ };
+ /* Affine projection matrix for z-near = -1, z-far = 1. */
+ static D3DMATRIX proj_mat =
+ {
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 0.0f,
+ 0.0f, 0.0f, 0.5f, 1.0f
+ };
+
+ window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW,
+ 0, 0, 640, 480, 0, 0, 0, 0);
+
+ if (!(device = create_device(window, DDSCL_NORMAL)))
+ {
+ skip("Failed to create a 3D device, skipping test.\n");
+ DestroyWindow(window);
+ return;
+ }
+
+ memset(&caps, 0, sizeof(caps));
+ hr = IDirect3DDevice7_GetCaps(device, &caps);
+ ok(SUCCEEDED(hr), "IDirect3DDevice7_GetCaps failed, hr %#x.\n", hr);
+
+ has_table_fog_support = caps.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGTABLE;
+ if (!has_table_fog_support)
+ skip("No table fog support, skipping some fog tests.\n");
+
+ hr = IDirect3DDevice7_GetRenderTarget(device, &rt);
+ ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr);
+
+ hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGENABLE, TRUE);
+ ok(SUCCEEDED(hr), "Failed to enable fog, hr %#x.\n", hr);
+ hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGCOLOR, C_FOGGED);
+ ok(SUCCEEDED(hr), "Failed to set fog color, hr %#x.\n", hr);
+ hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_LIGHTING, FALSE);
+ ok(SUCCEEDED(hr), "Failed to disable lighting, hr %#x.\n", hr);
+ hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE);
+ ok(SUCCEEDED(hr), "Failed to disable depth test, hr %#x.\n", hr);
+
+ hr = IDirect3DDevice7_SetTransform(device, D3DTRANSFORMSTATE_PROJECTION, &proj_mat);
+ ok(SUCCEEDED(hr), "Failed to set projection transform, hr %#x.\n", hr);
+
+ for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++)
+ {
+ if (test_data[i].tablemode != D3DFOG_NONE && !has_table_fog_support)
+ continue;
+
+ hr = IDirect3DDevice7_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0);
+ ok(SUCCEEDED(hr), "Failed to clear render target, i=%d, hr %#x.\n", i, hr);
+
+ hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGVERTEXMODE, test_data[i].vertexmode);
+ ok(SUCCEEDED(hr), "Failed to set fog vertex mode, i=%d, hr %#x.\n", i, hr);
+ hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGTABLEMODE, test_data[i].tablemode);
+ ok(SUCCEEDED(hr), "Failed to set fog table mode, i=%d, hr %#x.\n", i, hr);
+
+ hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGSTART,
+ *(DWORD*)(&test_data[i].fog_start));
+ ok(SUCCEEDED(hr), "Failed to set fog start, i=%d, hr %#x.\n", i, hr);
+ hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGEND,
+ *(DWORD*)(&test_data[i].fog_end));
+ ok(SUCCEEDED(hr), "Failed to set fog end, i=%d, hr %#x.\n", i, hr);
+
+ hr = IDirect3DDevice7_BeginScene(device);
+ ok(SUCCEEDED(hr), "Failed to begin scene, i=%d, hr %#x.\n", i, hr);
+ hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP,
+ D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, top_quad, 4, 0);
+ ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr);
+ hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP,
+ D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, bottom_quad_1, 4, 0);
+ ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr);
+ hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP,
+ D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, bottom_quad_2, 4, 0);
+ ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr);
+ hr = IDirect3DDevice7_EndScene(device);
+ ok(SUCCEEDED(hr), "Failed to end scene, i=%d, hr %#x.\n", i, hr);
+
+ /* Use 5% tolerance on the colors since there may be a gradient
+ * between left and right vertices. */
+ for (y = 120; y <= 360; y += 240)
+ {
+ color = get_surface_color(rt, 2, y);
+ ok(compare_color(color, test_data[i].color_left, 13),
+ "fog %.1f-%.1f fvm%i ftm%i y=%i: got left color %08x, expected %08x+-5%%.\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vertexmode,
+ test_data[i].tablemode, y, color, test_data[i].color_left);
+ color = get_surface_color(rt, 320, y);
+ expected_middle_color =
+ y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom;
+ ok(compare_color(color, expected_middle_color, 13),
+ "fog %.1f-%.1f fvm%i ftm%i y=%i: got middle color %08x, expected %08x+-5%%.\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vertexmode,
+ test_data[i].tablemode, y, color, expected_middle_color);
+ color = get_surface_color(rt, 638, y);
+ ok(compare_color(color, test_data[i].color_right, 13),
+ "fog %.1f-%.1f fvm%i ftm%i y=%i: got right color %08x, expected %08x+-5%%.\n",
+ test_data[i].fog_start, test_data[i].fog_end, test_data[i].vertexmode,
+ test_data[i].tablemode, y, color, test_data[i].color_right);
+ }
+ }
+
+ IDirectDrawSurface7_Release(rt);
+ refcount = IDirect3DDevice7_Release(device);
+ ok(!refcount, "Device has %u references left.\n", refcount);
+ DestroyWindow(window);
+}
+
static void test_lighting_interface_versions(void)
{
IDirect3DDevice7 *device;
@@ -8060,6 +8233,7 @@ START_TEST(ddraw7)
test_clear_rect_count();
test_coop_level_versions();
test_fog_special();
+ test_fog_negative_z();
test_lighting_interface_versions();
test_coop_level_activateapp();
test_texturemanage();
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index 98239ef..fab7251 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -5007,7 +5007,7 @@ static GLhandleARB shader_glsl_generate_ffp_vertex_shader(struct wined3d_shader_
/* Need to undo the [0.0 - 1.0] -> [-1.0 - 1.0] transformation from D3D to GL coordinates. */
shader_addline(buffer, "gl_FogFragCoord = gl_Position.z * 0.5 + 0.5;\n");
else
- shader_addline(buffer, "gl_FogFragCoord = ec_pos.z;\n");
+ shader_addline(buffer, "gl_FogFragCoord = abs(ec_pos.z);\n");
break;
default:
--
2.1.3

View File

@ -1 +0,0 @@
Fixes: Take abs() of vertex z coordinate as FFP fog coordinate